libves
Version:
Simple, safe and secure end-to-end encryption at-rest
1,444 lines (1,427 loc) • 189 kB
JavaScript
/***************************************************************************
* ___ ___
* / \ / \ VESvault
* \__ / \ __/ Encrypt Everything without fear of losing the Key
* \\ // https://vesvault.com https://ves.host
* \\ //
* ___ \\_//
* / \ / \ libVES: VESvault API library
* \__ / \ __/
* \\ //
* \\ //
* \\_// - Key Management and Exchange
* / \ - Item Encryption and Sharing
* \___/ - VESrecovery (TM)
*
*
* (c) 2017 - 2022 VESvault Corp
* Jim Zubov <jz@vesvault.com>
*
* GNU General Public License v3
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* @title libVES
* @dev A JavaScript end-to-end encryption interface to VESvault REST API
* @version 3.02
*
* @dev Official source code: https://github.com/vesvault/libVES
*
* @author Jim Zubov <jz@vesvault.com> (VESvault Corp)
*
***************************************************************************/
crypto = require('crypto');
crypto.subtle = require('subtle');
XMLHttpRequest = require('xhr2');
if (typeof(libVES) != 'function') function libVES(optns) {
try {
if (!crypto.subtle.digest) throw new libVES.Error('Init', 'crypto.subtle is unavailable or improperly implemented');
} catch (e) {
if (e instanceof libVES.Error) throw e;
throw new libVES.Error('Init', 'crypto.subtle is not available');
}
for (var k in optns) this[k] = optns[k];
if (this.domain) this.type = 'secondary';
else if (this.user) this.type = 'primary';
else throw new libVES.Error('InvalidValue','Required parameters: user || domain');
this.unlockedKeys = {};
this.pendingKeys = {};
}
libVES.prototype = {
constructor: libVES,
apiUrl: 'https://api.ves.host/v1/',
pollUrl: 'https://poll.ves.host/v1/',
wwwUrl: 'https://www.vesvault.com/',
keyAlgo: 'ECDH',
keyOptions: {namedCurve: 'P-521'},
textCipher: 'AES256GCMp',
defaultHash: 'SHA256',
propagators: null,
request: function(method,uri,body,optns) {
var self = this;
if (!optns) optns = {};
return new Promise(function(resolve,reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, (uri.match(/^https\:/) ? uri : this.apiUrl + uri));
if (optns.abortFn) optns.abortFn(function() {
return xhr.abort();
});
xhr.onreadystatechange = function() {
switch(xhr.readyState) {
case 4:
if (xhr.response && typeof(xhr.response) == 'object') {
if (xhr.response.errors) {
var errs = xhr.response.errors.map(function(e) {
return new libVES.Error(e.type,e.message,e);
});
if (errs.length) {
var retry = function(o) { return self.request(method, uri, body, (o || optns)); };
if (optns && optns.onerror) try {
resolve(optns.onerror(errs, optns, retry));
} catch (e) {
reject(e);
} else if (self.onerror) try {
resolve(self.onerror(errs, optns, retry));
} catch (e) {
reject(e);
}
else reject(errs[0]);
}
}
else resolve(xhr.response.result);
} else reject(new libVES.Error('BadResponse','Empty response'));
}
};
if (body != null) xhr.setRequestHeader('Content-Type','application/json');
xhr.setRequestHeader('Accept','application/json');
if (this.user && optns.password) xhr.setRequestHeader('Authorization','Basic ' + btoa(this.user + ':' + optns.password));
else if (optns.token ?? this.token) xhr.setRequestHeader('Authorization','Bearer ' + (optns.token ? optns.token : this.token));
xhr.responseType = 'json';
xhr.send(body);
}.bind(this));
},
get: function(uri,fields,optns) {
return this.request('GET',this.uriWithFields(uri,fields),null,optns);
},
post: function(uri,data,fields,optns) {
return this.request('POST',this.uriWithFields(uri,fields),JSON.stringify(data),optns);
},
uriWithFields: function(uri,fields) {
return fields ? uri + (uri.match(/\?/) ? '&' : '?') + 'fields=' + this.uriListFields(fields) : uri;
},
uriListFields: function(fields) {
if (typeof(fields) == 'object') {
var rs = [];
if (fields[0]) rs = fields;
else for (var k in fields) {
if (fields[k]) rs.push(k + (typeof(fields[k]) == 'object' ? '(' + this.uriListFields(fields[k]) + ')' : ''));
}
return rs.join(',');
}
return '';
},
mergeFieldList: function(flds, flds2) {
for (var k in flds2) if (flds[k] instanceof Object) flds[k] = this.mergeFieldList(flds[k], flds2[k]); else flds[k] = flds2[k];
return flds;
},
elevateAuth: function(optns) {
var self = this;
if (optns && (optns.password || optns.token)) return Promise.resolve(optns);
return (optns && optns.authVaultKey ? Promise.resolve(optns.authVaultKey) : self.getVaultKey()).then(function(vkey) {
return vkey.getSessionToken().then(function(tkn) {
if (!tkn) return optns;
var o = {token: tkn};
if (optns) for (var k in optns) o[k] = optns[k];
return o;
});
});
},
login: function(passwd) {
if (this.token) return this.me();
var self = this;
return this.userMe = Promise.resolve(passwd).then(function(passwd) {
return self.get('me',{sessionToken: true},{password: passwd}).then(function(data) {
if (!data.sessionToken) throw new libVES.Error('InvalidValue','Session Token is not received');
self.token = data.sessionToken;
return new libVES.User(data,self);
});
});
},
logout: function() {
this.token = this.userMe = this.vaultKey = undefined;
return this.lock();
},
delegate: function(optns) {
var self = this;
return libVES.getModule(libVES,'Delegate').then(function(dlg) {
return dlg.login(self,null,optns).then(function() {
return self;
});
});
},
getVESflow: function() {
var self = this;
if (!this.VESflow) this.VESflow = libVES.getModule(libVES, 'Flow').then(function(fw) {
var flow = new fw('VES');
var authf = self.authorize.bind(self);
self.authorize = function(msg) {
flow.setValue(msg);
return authf(msg);
};
var outf = self.logout.bind(self);
self.logout = function() {
flow.logout();
return outf();
};
return flow;
});
return this.VESflow;
},
flow: function(start, optns) {
var self = this;
return self.getVESflow().then(function(flow) {
return flow.get().then(function(auth) {
return self.authorize(auth);
}).catch(function(e) {
if (!start || (e && e.code == 'Reload')) throw e;
var url = self.wwwUrl + 'vv/unlock?url=' + encodeURIComponent(document.location.href) + '&domain=' + encodeURIComponent(self.domain);
if (optns) for (var k in optns) if (optns[k] != null) url += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(optns[k]);
flow.url = undefined;
return flow.setValue(undefined).then(function() {
return flow.addToken(url).then(function(url) {
document.location.replace(url);
throw new libVES.Error('Redirect', 'Starting VES Authorization...');
});
});
});
});
},
authorize: function(msg) {
var self = this;
return this.logout().then(function() {
if (msg.token) self.token = msg.token;
if (msg.domain) self.domain = msg.domain;
if (msg.externalId) self.externalId = msg.externalId;
return (msg.VESkey ? self.unlock(msg.VESkey) : self.me()).then(function() {
return self;
});
});
},
carry: function(optns) {
var cr = {};
if (this.externalId) cr.externalId = this.externalId;
if (this.domain) cr.domain = this.domain;
if (this.token) cr.token = this.token;
if (this.VESkey) cr.VESkey = this.VESkey;
try {
sessionStorage['libVES_carry'] = JSON.stringify(cr);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(true);
},
pick: function(optns) {
try {
if (!sessionStorage['libVES_carry']) throw new libVES.Error('NotFound','No libVES_carry in sessionStorage');
var cr = JSON.parse(sessionStorage['libVES_carry']);
delete(sessionStorage['libVES_carry']);
return this.authorize(cr);
} catch (e) {
return Promise.reject(e);
}
},
me: function() {
var self = this;
if (!this.userMe) this.userMe = this.get('me').then((function(data) {
return new libVES.User(data,self);
}).bind(this));
return this.userMe;
},
unlock: function(veskey) {
var self = this;
return this.getVaultKey().then(function(vkey) {
return vkey.unlock(veskey).then(function(cryptoKey) {
if (self.VESkey === null) self.VESkey = veskey;
if (!self.token && self.type == 'secondary') return vkey.getSessionToken().then(function(tkn) {
self.token = tkn;
return cryptoKey;
});
return cryptoKey;
});
}).then(function(ck) {
return self.handleAttn().catch(function() {}).then(function() {
return ck;
});
});
},
lock: function() {
var lock = [];
for (var kid in this.unlockedKeys) lock.push(this.unlockedKeys[kid].then(function(k) {
return k.lock();
}).catch(function() {}));
this.propagators = null;
return Promise.all(lock).then(function() {
return true;
});
},
reset: function(val) {
this.userMe = undefined;
return this.lock().then(function() {
return val;
});
},
getVaultKey: function() {
var self = this;
switch (this.type) {
case 'primary': return this.me().then(function(me) {
return me.getCurrentVaultKey();
});
case 'secondary': return (this.vaultKey || (this.vaultKey = this.prepareExternals({externalId: self.externalId}).then(function(ext) {
var vKey = new libVES.VaultKey({type: 'secondary', externals: ext},self);
return vKey.getField('encSessionToken').then(function(tk) {
return vKey;
});
})));
default: throw new libVES.Error('Internal','Invalid libVES.type: ' + this.type);
}
},
getShadowKey: function() {
return this.me().then(function(me) {
return me.getShadowVaultKey();
});
},
getVaultKeysById: function() {
return this.me().then(function(me) {
return me.getVaultKeys().then(function(vaultKeys) {
return Promise.all(vaultKeys.map(function(e,i) {
return e.getId();
})).then(function(ids) {
var rs = {};
for (var i = 0; i < ids.length; i++) rs[ids[i]] = vaultKeys[i];
return rs;
});
});
});
},
getItems: function(flds) {
var self = this;
return this.getVaultKey().then(function(k) {
return k.getId().then(function(kid) {
return k.getVaultEntries(self.mergeFieldList({type: true, deleted: true, file: {creator: true, externals: true}, vaultKey: {type: true, user: true}}, flds)).then(function(ves) {
var vis = {};
var vlst = [];
for (var i = 0; i < ves.length; i++) {
var viid = ves[i].vaultItem.id;
if (!vis[viid]) {
var vi = ves[i].vaultItem;
if (!vi.file) vi.file = undefined;
if (!vi.vaultKey) vi.vaultKey = undefined;
vi = vis[viid] = self.getItem(vi);
vlst.push(vi);
if (!ves[i].vaultKey) ves[i].vaultKey = {id: kid};
vi.vaultEntryByKey[kid] = ves[i];
}
}
return vlst;
});
});
});
},
getItem: function(data) {
return new libVES.VaultItem(data,this);
},
postItem: function(data) {
var vi = new libVES.VaultItem(data,this);
return vi.validate().then(function() {
return vi.post();
});
},
usersToKeys: function(users) {
var self = this;
return Promise.all(users.map(function(u) {
if (typeof(u) == 'object') {
if (u instanceof libVES.VaultKey) return [u];
else if (u instanceof libVES.External) return [new libVES.VaultKey({externals:[u]},self)];
else if (u instanceof libVES.User) return self.getUserKeys(u);
else if (u instanceof Array || u.domain != null || u.externalId != null) return self._matchSecondaryKey(u, u.user, u.appUrl).then(function(vkey) {
return [vkey];
});
}
return self.getUserKeys(self._matchUser(u));
})).then(function(ks) {
var rs = [];
for (var i = 0; i < ks.length; i++) for (var j = 0; j < ks[i].length; j++) rs.push(ks[i][j]);
return rs;
});
},
_matchUser: function(u) {
if (typeof(u) == 'object') {
if (u instanceof libVES.User) return u;
else return new libVES.User((u.id ? {id: u.id} : {email: u.email}), this);
} else if (typeof(u) == 'string' && u.match(/^\S+\@\S+$/)) return new libVES.User({email: u},this);
throw new libVES.Error('BadUser',"Cannot match user: " + u,{value: u});
},
_matchSecondaryKey: function(ext, user, appUrl) {
var self = this;
var m = function() {
return libVES.getModule(libVES.Domain,ext.domain).catch(function(e) {
return {
userToVaultRef: function(u) {
return u.getEmail().then(function(email) {
return email.toLowerCase();
});
},
vaultRefToUser: function(ext) {
var email = ext.externalId ? ext.externalId.replace(/\!.*/, '') : '';
if (!email.match(/\@/)) throw new libVES.Error('NotFound', 'externalId is not an email for non-existing vault');
return new libVES.User({email: email}, self);
}
};
});
};
return (ext.externalId ? self.prepareExternals(ext) : m().then(function(dom) {
return Promise.resolve(user || self.me()).then(function(u) {
return dom.userToVaultRef(u);
}).then(function(ex) {
return self.prepareExternals([ex]);
});
}).catch(function(e) {
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});
})).then(function(exts) {
var keyref = {externals: exts};
if (self.externalId && self.externalId[0] != '!') keyref.creator = self.me();
var vkey = new libVES.VaultKey(keyref, self);
return vkey.getId().then(function() {
return vkey;
}).catch(function(e) {
if (e.code != 'NotFound') throw e;
if (!keyref.creator) throw new libVES.Error.InvalidKey('Anonymous vaults are not authorized to create temp keys');
return Promise.resolve(user || m().then(function(dom) {
return dom.vaultRefToUser(exts[0]);
})).catch(function(e) {
throw new libVES.Error('NotFound', 'No matching secondary vault key',{error: e});
}).then(function(u) {
return self.me().then(function(me) {
return Promise.all([me.getId(),u.getId()]).then(function(ids) {
if (ids[0] == ids[1]) return self.getSecondaryKey(exts,true);
}).catch(function(e) {
if (e.code != 'NotFound') throw e;
}).then(function(rs) {
return rs || self.createTempKey(self._matchUser(u)).then(function(vkey) {
return vkey.setField('externals',exts).then(function() {
if (appUrl) vkey.setField('appUrl', appUrl);
return vkey;
});
});
});
});
});
});
});
},
getPropagators: function() {
if (!this.propagators) {
let xid = (this.externalId ?? this.email);
this.propagators = (xid?.match(/^[^\!]+\@\w/) ? Promise.resolve(xid.replace(/\!.*/, '')) : this.me().then((me) => me.getEmail())).then((xid) => {
if (!xid) return [];
let prop = new libVES.VaultKey({externals: {domain: '.propagate', externalId: xid.toLowerCase()}}, this);
return prop.getId().then(() => [prop]);
}).catch((er) => {
if (er?.code == 'NotFound' || er?.code == 'Unauthorized') return [];
throw er;
});
}
return Promise.resolve(this.propagators);
},
getUserKeys: function(usr) {
var self = this;
return usr.getActiveVaultKeys().catch(function(e) {
if (e.code == 'NotFound') return [];
throw e;
}).then(function(keys) {
return Promise.all(keys.map(function(k,i) {
return k.getPublicCryptoKey().then(function() {
return k;
}).catch(function() {});
})).then(function(keys) {
var rs = [];
for (var i = 0; i < keys.length; i++) if (keys[i]) rs.push(keys[i]);
return rs;
});
}).then(function(keys) {
if (!keys.length) return self.createTempKey(usr).then(function(k) {
return [k];
});
return keys;
});
},
createTempKey: function(usr, optns) {
var self = this;
var key = new libVES.VaultKey({type: 'temp', algo: this.keyAlgo, user: usr}, self);
var veskey = this.generateVESkey(usr);
return key.generate(veskey, optns).then(function(k) {
key.setField('vaultItems', veskey.then(function(v) {
var vi = new libVES.VaultItem({type: 'password'}, self);
return usr.getActiveVaultKeys().then(function(akeys) {
return [self.me(), self.getVaultKey()].concat(akeys);
}).catch(function(e) {
if (e.code != 'NotFound') throw e;
var sh = self.type == 'secondary' ? [self.me(), self.getVaultKey()] : [self.me()];
return self.getPropagators().then((props) => sh.concat(props)).catch((er) => sh);
}).then(function(sh) {
return Promise.all(sh);
}).then(function(sh) {
return vi.shareWith(sh, v, false).then(function() {
return [vi];
});
});
}));
key.setField('creator', self.me());
return key;
});
},
generateVESkey: function(usr) {
var buf = new Uint8Array(24);
crypto.getRandomValues(buf);
return Promise.resolve(libVES.Util.ByteArrayToB64(buf));
},
setVESkey: function(veskey,lost,options) {
var self = this;
return this.me().then(function(me) {
return (new libVES.VaultKey({type: 'current', algo: self.keyAlgo, user: me},self)).generate(Promise.resolve(veskey),options).then(function(k) {
return me.getCurrentVaultKey().then(function(cur) {
return (cur ? cur.unlock().then(function() {
return k.rekeyFrom(cur);
}).catch(function(e) {
if (!lost) throw e;
}) : Promise.resolve(null)).then(function() {
var r;
if (cur && lost) r = cur.setField('type','lost').then(function() {
k.user = undefined;
return me.setField('vaultKeys',[cur,k]).then(function() {
return me;
});
});
else r = k;
me.currentVaultKey = me.activeVaultKeys = me.shadowVaultKey = undefined;
if (!cur || !lost) me.vaultKeys = undefined;
return r;
});
}).then(function(r) {
return self.elevateAuth(options).then(function(optns) {
return r.post(undefined, undefined, optns);
});
}).catch(function(e) {
self.reset();
throw e;
}).then(function(post) {
return self.reset(post);
}).then(function() {
return self.getVaultKey();
});
});
});
},
prepareExternals: function(ext) {
var self = this;
if (!ext) return Promise.reject(new libVES.Error('InvalidValue','External reference is required'));
return Promise.resolve(ext).then(function(ext) {
if (!(ext instanceof Array)) ext = [ext];
if (ext.length < 1) throw new libVES.Error('InvalidValue','External reference is required');
var rs = [];
for (var i = 0; i < ext.length; i++) {
rs[i] = (typeof(ext[i]) == 'object') ? {externalId: ext[i].externalId, domain: ext[i].domain} : {externalId: ext[i]};
if (!rs[i].domain && !(rs[i].domain = self.domain)) throw new libVES.Error('InvalidValue','External reference: domain is required');
if (!rs[i].externalId) throw new libVES.Error('InvalidValue','External reference: externalId is required');
}
return rs;
});
},
getSecondaryKey: function(ext, force) {
var self = this;
return this.prepareExternals(Promise.resolve(ext).then(function(e) {
if (e.domain && !e.externalId) return libVES.getModule(libVES.Domain, e.domain).then(function(mod) {
return self.me().then(function(me) {
return mod.userToVaultRef(me, self);
});
});
return e;
})).then(function(ext) {
var vkey = new libVES.VaultKey({externals: ext}, self);
return vkey.getId().then(function(id) {
return vkey;
}).catch(function(e) {
if (!force || e.code != 'NotFound') throw e;
return self.setSecondaryKey(ext);
});
});
},
setSecondaryKey: function(ext,veskey,optns) {
var self = this;
return this.prepareExternals(ext).then(function(ext) {
if (!veskey) veskey = self.generateVESkey();
return self.me().then(function(me) {
return (new libVES.VaultKey({type: 'secondary', algo: self.keyAlgo, user: me, externals: ext},self)).generate(veskey,optns).then(function(k) {
return self.getSecondaryKey(ext).then(function(k2) {
return k.rekeyFrom(k2);
}).catch(function(e) {
delete(k.vaultEntries);
return k;
});
}).then(function(k) {
var vi = new libVES.VaultItem({type: "password"},self);
k.setField('vaultItems',[vi]);
return Promise.resolve(veskey).then(function(v) {
if (!v) throw new libVES.Error('InvalidValue','VESkey cannot be empty');
return vi.shareWith([me],v,false).then(function() {
return self.elevateAuth(optns);
}).then(function(optns) {
return k.post(undefined, undefined, optns).then(function(post) {
return k.setField('id', post.id, false).then(function() {
k.fieldUpdate = {id: true};
return k;
});
});
});
});
});
});
});
},
setAnonymousKey: function(veskey, optns) {
var self = this;
return this.prepareExternals({externalId: self.externalId}).then(function(ext) {
if (!veskey) throw new libVES.Error('InvalidValue','VESkey cannot be empty');
return (new libVES.VaultKey({type: 'secondary', algo: (optns && optns.algo ? optns.algo : self.keyAlgo), externals: ext},self)).generate(veskey, optns).then(function(k) {
return k.post(undefined, ['encSessionToken'], optns).then(function(post) {
return k.setField('id', post.id, false).then(function() {
k.fieldUpdate = {id: true};
});
}).catch(function(e) {
if (!e || e.code != 'Unauthorized') throw e;
}).then(function() {
self.vaultKey = k.lock().then(function() {
return k;
});
return self.unlock(veskey);
});
});
});
},
setShadow: function(usrs,optns) {
var self = this;
if (!(usrs instanceof Array)) return Promise.reject(new libVES.Error('InvalidValue', 'usrs must be an array'));
if (!usrs.length) {
return self.getShadowKey().then(function(sh) {
return (sh ? sh.getId().then(function(id) {
return self.elevateAuth(optns).then(function(optns) {
return self.post('vaultKeys/' + id, {'$op': 'delete'}, undefined, optns). then(function() {
return null;
});
});
}) : null);
});
}
if (!optns || !optns.n) return Promise.reject(new libVES.Error('InvalidValue','optns.n must be an integer'));
var rkey = new Uint8Array(32);
crypto.getRandomValues(rkey);
var algo = optns.v ? libVES.Scramble.algo[optns.v] : libVES.Scramble.RDX;
if (!algo) return Promise.reject(new libVES.Error('InvalidValue','Unknown scramble algorithm: ' + optns.v));
var s = new algo(optns.n);
return s.explode(rkey,usrs.length,optns).then(function(tkns) {
return self.me().then(function(me) {
me.activeVaultKeys = undefined;
return me.setField('shadowVaultKey',new libVES.VaultKey({type: 'shadow', user: me, algo: self.keyAlgo},self).generate(rkey,optns),false).then(function(k) {
return me.getCurrentVaultKey().then(function(curr) {
return k.rekeyFrom(curr).catch(function() {}).then(function() {
libVES.Object._refs = {"#/":k};
k.setField('vaultItems',Promise.all(tkns.map(function(tk,i) {
var vi = new libVES.VaultItem({type: 'secret'},self);
return vi.shareWith([usrs[i]],tk,false).then(function() {
return vi;
});
})).then(function(vis) {
delete(libVES.Object._refs);
return vis;
}));
return self.elevateAuth(optns).then(function(optns) {
return k.post(undefined, undefined, optns);
});
});
});
}).catch(function(e) {
me.shadowVaultKey = undefined;
throw e;
}).then(function() {
me.currentVaultKey = me.shadowVaultKey = me.activeVaultKeys = undefined;
return me.getShadowVaultKey();
});
});
});
},
getFile: function(fileRef) {
var self = this;
return self.prepareExternals(fileRef).then(function(ext) {
return new libVES.File({externals: ext},self);
});
},
getFileItem: function(fileRef) {
var self = this;
return self.getFile(fileRef).then(function(file) {
return new libVES.VaultItem({file: file},self);
});
},
getValue: function(fileRef) {
return this.getFileItem(fileRef).then(function(vaultItem) {
return vaultItem.get();
});
},
putValue: function(fileRef,value,shareWith) {
var self = this;
return this.getFileItem(fileRef).then(function(vaultItem) {
return vaultItem.setField('type',libVES.VaultItem.Type._detect(value)).then(function() {
return Promise.resolve(shareWith || self.getFileItem(fileRef).then(function(vi) {
return vi.getShareList();
}).catch(function(e) {
return self.usersToKeys([{domain: VES.domain, externalId: VES.externalId}]);
})).then(function(shareWith) {
return vaultItem.shareWith(shareWith,value);
});
});
});
},
shareFile: function(fileRef,shareWith) {
return this.getFileItem(fileRef).then(function(vaultItem) {
return vaultItem.shareWith(shareWith);
});
},
fileExists: function(fileRef) {
return this.getFileItem(fileRef).then(function(vaultItem) {
return vaultItem.getId().then(function(id) {
return true;
}).catch(function(e) {
if (e.code == 'NotFound') return false;
throw e;
});
});
},
deleteFile: function(fileRef) {
return this.getFileItem(fileRef).then(function(item) {
return item.delete();
});
},
newSecret: function(cls) {
if (!cls) cls = this.textCipher;
else cls = cls.split('.')[0];
return libVES.getModule(libVES.Cipher,cls).then(function(ci) {
return (new ci()).getSecret().then(function(buf) {
return cls + '.' + libVES.Util.ByteArrayToB64W(buf);
});
});
},
secretToCipher: function(secret) {
var ss = secret.split('.');
return libVES.getModule(libVES.Cipher,ss[0]).then(function(cls) {
return new cls(libVES.Util.B64ToByteArray(ss[1]));
});
},
encryptText: function(openText,secret) {
return this.secretToCipher(secret).then(function(ci) {
return ci.encrypt(libVES.Util.StringToByteArray(openText),true).then(function(buf) {
return libVES.Util.ByteArrayToB64W(buf);
});
});
},
decryptText: function(cipherText,secret) {
return this.secretToCipher(secret).then(function(ci) {
return ci.decrypt(libVES.Util.B64ToByteArray(cipherText),true).then(function(buf) {
return libVES.Util.ByteArrayToString(buf);
});
});
},
hashText: function(text,cls) {
if (cls) cls = cls.split('.')[0];
else cls = this.defaultHash;
return libVES.getModule(libVES.Util,['Hash',cls]).then(function(mod) {
return mod.hash(libVES.Util.StringToByteArray(text)).then(function(buf) {
return cls + '.' + libVES.Util.ByteArrayToB64W(buf);
});
});
},
found: function(veskeys,vaultKeys) {
var self = this;
return Promise.resolve(veskeys).then(function(veskeys) {
var chain = Promise.resolve(0);
if (veskeys && !(veskeys instanceof Array)) veskeys = [veskeys];
return (vaultKeys ? Promise.resolve(vaultKeys) : self.me().then(function(me) {
var rs = [];
return me.getVaultKeys().then(function(vaultKeys) {
return Promise.all(vaultKeys.map(function(vaultKey,i) {
return vaultKey.getType().then(function(t) {
switch (t) {
case 'temp': case 'lost': case 'recovery': rs.push(vaultKey);
}
});
}));
}).then(function() {
return rs;
});
})).then(function(vaultKeys) {
if (!(vaultKeys instanceof Array)) vaultKeys = [vaultKeys];
return Promise.all(vaultKeys.map(function(vaultKey,i) {
return vaultKey.getRecovery().then(function(rcv) {
return rcv.unlock();
}).catch(function(e) {
var rs = vaultKey.unlock();
if (veskeys) veskeys.map(function(veskey,i) {
rs = rs.catch(function() {
return vaultKey.unlock(veskey);
});
});
return rs;
}).then(function() {
chain = chain.then(function(ct) {
return vaultKey.rekey().then(function() {
return ct + 1;
});
}).catch(function(e) {console.log(e);});;
}).catch(function() {});
}));
}).then(function() {
return chain;
});
}).then(function(ct) {
if (ct) return ct + self.found();
});
},
getMyRecoveries: function() {
var self = this;
return self.me().then(function(me) {
return me.getVaultKeys().then(function(vaultKeys) {
return Promise.all(vaultKeys.map(function(e,i) {
return e.getType();
})).then(function(types) {
var rs = [];
for (var i = 0; i < types.length; i++) switch (types[i]) {
case 'recovery': case 'shadow':
rs.push(vaultKeys[i].getRecovery());
}
return Promise.all(rs);
});
});
});
},
getFriendsRecoveries: function() {
var self = this;
return self.me().then(function(me) {
return me.getFriendsKeyItems().then(function(vaultItems) {
var vaultKeys = [];
return Promise.all(vaultItems.map(function(e,i) {
return e.getVaultKey().then(function(vk) {
vaultKeys[i] = vk;
if (vk) return vk.getType();
});
})).then(function(types) {
var rs = [];
for (var i = 0; i < types.length; i++) switch (types[i]) {
case 'recovery': case 'shadow':
rs.push(vaultKeys[i].getRecovery([vaultItems[i]]));
}
return Promise.all(rs);
});
});
});
},
getUnlockableKeys: function() {
var self = this;
return self.getVaultKey().then(function(vk) {
return vk.getField('unlockableVaultKeys').then(function(vks) {
var rs = {};
return Promise.all((vks || []).map(function(e, i) {
return e.getId().then(function(id) {
rs[id] = e;
});
})).then(function() {
return rs;
});
});
});
},
getAttn: function() {
var self = this;
var rs = {};
return self.get('attn').then(function(attn) {
return attn ? Promise.all([(attn.vaultKeys ? Promise.all(attn.vaultKeys.map(function(vk, i) {
return new libVES.VaultKey(vk, self);
})).then(function(vks) {
rs.vaultKeys = vks;
}) : null)]) : null;
}).then(function() {
return rs;
});
},
handleAttn: function(attn) {
var self = this;
return (attn ? Promise.resolve(attn) : self.getAttn()).then(function(attn) {
if (attn) return Promise.all([(attn.vaultKeys ? self.attnVaultKeys(attn.vaultKeys) : null)]);
});
},
attnVaultKeys: function(vkeys) {
var self = this;
return Promise.all(vkeys.map(function(vkey, i) {
console.log('attnVaultKeys:', vkey);
return vkey.getType().then(function(type) {
switch (type) {
case 'temp':
case 'lost':
return vkey.getUser().then(function(user) {
return self.me().then(function(me) {
return Promise.all([user.getId(), me.getId()]).then(function(ids) {
if (ids[0] == ids[1]) return type == 'temp' ? vkey.rekey()
: self.elevateAuth({authVaultKey: vkey}).then(function(optns) {
return vkey.rekey(optns);
});
else return vkey.getVaultItems().then(function(vis) {
return Promise.all([user.getActiveVaultKeys(), vkey.getExternals().then((exts) => {
let ckey = new libVES.VaultKey({externals: exts}, self);
return ckey.getType().then(() => [ckey]).catch((er) => {
if (er?.code != 'NotFound') throw er;
return [];
});
})]).then((klists) => {
let ks = (klists[0] ?? []).concat(klists[1]);
return Promise.all(ks.map((k) => k.getType())).then((ktypes) => ks.filter((k, i) => ktypes[i] != 'temp'));
}).then((ks) => self.elevateAuth().then(function(optns) {
return Promise.all(vis.map(function(vi, i) {
return vi.reshareWith(ks, undefined, optns);
}));
}));
});
});
});
});
case 'recovery':
return vkey.getRecovery().then(function(rcv) {
return rcv.recover();
});
}
});
}));
},
setKeyAlgo: function(optns) {
if (typeof(optns) == 'string') {
this.keyOptions = null;
return this.keyAlgo = optns;
}
var algo = libVES.Algo.fromKeyOptions(optns);
if (algo) {
this.keyOptions = optns;
return this.keyAlgo = algo;
}
}
};
libVES.Error = function(code, msg, optns) {
if (libVES.Error[code] && (libVES.Error[code].prototype instanceof libVES.Error)) return new (libVES.Error[code])(msg, optns);
this.code = code;
this.init(msg, optns);
};
libVES.Error.prototype.init = function(msg, optns) {
this.message = msg;
if (optns) for (var k in optns) this[k] = optns[k];
};
libVES.Error.prototype.toString = function() {
return this.message || this.code;
};
libVES.Error.NotFound = function(msg, optns) {
this.init(msg, optns);
};
libVES.Error.NotFound.prototype = new libVES.Error('NotFound');
libVES.Error.InvalidValue = function(msg, optns) {
this.init(msg, optns);
};
libVES.Error.InvalidValue.prototype = new libVES.Error('InvalidValue');
libVES.Error.InvalidKey = function(msg, optns) {
this.init(msg, optns);
};
libVES.Error.InvalidKey.prototype = new libVES.Error('InvalidKey');
libVES.Error.Redirect = function(msg, optns) {
this.init(msg, optns);
};
libVES.Error.Redirect.prototype = new libVES.Error('Redirect');
libVES.Error.Unauthorized = function(msg, optns) {
this.init(msg, optns);
};
libVES.Error.Unauthorized.prototype = new libVES.Error('Unauthorized');
libVES.Error.Internal = function(msg, optns) {
this.init(msg, optns);
};
libVES.Error.Internal.prototype = new libVES.Error('Internal');
libVES.getModule = function(sectn,mods) {
var mod;
if (mods instanceof Array) mod = mods[0];
else mods = [mod = mods];
if (sectn[mod]) return mods.length > 1 ? libVES.getModule(sectn[mod],mods.slice(1)) : Promise.resolve(sectn[mod]);
if (sectn.loadModule) {
if (sectn.loadModule[mod]) return sectn.loadModule[mod];
} else sectn.loadModule = {};
return sectn.loadModule[mod] = libVES.loadModule(sectn,mod).then(function(m) {
delete(sectn.loadModule[mod]);
sectn[mod] = m;
return ((mods instanceof Array) && mods.length > 1 ? libVES.getModule(m,mods.slice(1)) : m);
});
};
libVES.getModuleFunc = function(sectn,mod,then) {
return function() { var m = libVES.getModule(sectn,mod); return then ? m.then(then) : m; };
};
libVES.loadModule = function(sectn,mod) {
return Promise.reject(new libVES.Error('Internal',"Cannot load " + sectn + '.' + mod));
};
libVES.maxKeyLen = 48896;
libVES.maxEncDataLen = 32768;
if (!libVES.Domain) libVES.Domain = {};
libVES.Util = {
B64ToByteArray: function(s) {
var buf = new Uint8Array(s.length);
var boffs = 0;
for (var i = 0; i < s.length; i++) {
var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_".indexOf(s[i]);
if (p >= 0) {
if (p >= 64) p -= 2;
buf[boffs >> 3] |= p << 2 >> (boffs & 7);
boffs += 6;
if ((boffs & 7) < 6) buf[boffs >> 3] |= p << (8 - (boffs & 7));
}
}
var l = boffs >> 3;
var buf2 = new Uint8Array(l);
for (var i = 0; i < l; i++) buf2[i] = buf[i];
return buf2.buffer;
},
ByteArrayToB64D: function(b,dict) {
var buf = new Uint8Array(b);
var s = "";
var boffs = 0;
while ((boffs >> 3) < buf.byteLength) {
var c = (buf[boffs >> 3] << (boffs & 7)) & 0xfc;
boffs += 6;
if (((boffs & 7) < 6) && ((boffs >> 3) < buf.byteLength)) c |= (buf[boffs >> 3] >> (6 - (boffs & 7)));
s += dict[c >> 2];
}
for (; boffs & 7; boffs += 6) s += dict.substr(64);
return s;
},
ByteArrayToB64: function(b) {
return libVES.Util.ByteArrayToB64D(b,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
},
ByteArrayToB64W: function(b) {
return libVES.Util.ByteArrayToB64D(b,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
},
StringToByteArray: function(s) {
if ((s instanceof ArrayBuffer) || (s instanceof Uint8Array)) return s;
var rs = new Uint8Array(4 * s.length);
var j = 0;
for (var i = 0; i < s.length;i++) {
var c = s.charCodeAt(i);
if (c >= 0x80) {
if (c >= 0x0800) {
if (c >= 0x10000) {
rs[j++] = (c >> 16) | 0xf0;
rs[j++] = ((c >> 12) & 0x3f) | 0x80;
} else rs[j++] = ((c >> 12) & 0x0f) | 0xe0;
rs[j++] = ((c >> 6) & 0x3f) | 0x80;
} else rs[j++] = ((c >> 6) & 0x1f) | 0xc0;
rs[j++] = (c & 0x3f) | 0x80;
} else rs[j++] = c;
}
return rs.slice(0,j).buffer;
},
ByteArrayToString: function(b) {
var buf = new Uint8Array(b);
var rs = '';
var c;
for (var i = 0; i < buf.length; i++) {
var v = buf[i];
if (v & 0x80) {
if (v & 0x40) {
c = ((v & 0x1f) << 6) | (buf[++i] & 0x3f);
if (v & 0x20) {
c = (c << 6) | (buf[++i] & 0x3f);
if (v & 0x10) c = ((c & 0xffff) << 6) | (buf[++i] & 0x3f);
}
} else c = -1;
} else c = buf[i];
rs += String.fromCharCode(c);
}
return rs;
},
fillUndefs: function(data, defs) {
if (data instanceof Array) data.map((d) => libVES.Util.fillUndefs(d, defs));
else if (data && defs) for (var k in defs) if (defs[k]) {
if (data[k] === undefined) data[k] = undefined;
else if (data[k] instanceof Object) libVES.Util.fillUndefs(data[k], defs[k]);
}
return data;
},
loadWasm: function(src) {
return new Promise(function(resolve, reject) {
var sc = document.createElement('script');
sc.async = false;
sc.src = src;
sc.onload = resolve;
sc.onerror = reject;
document.getElementsByTagName('head')[0].appendChild(sc);
});
},
PEM: {
toDER: function(pem) {
var pp = pem.match(/-----BEGIN.*?-----\s*\r?\n([A-Za-z0-9\/\+\=\s\r\n]*)-----END/);
if (!pp) throw new libVES.Error('Internal','PEM formatted key expected');
return new Uint8Array(libVES.Util.B64ToByteArray(pp[1]));
},
decode: function(pem) {
return libVES.Util.ASN1.decode(libVES.Util.PEM.toDER(pem));
},
import: function(pem,optns) {
return libVES.Util.ASN1.import(libVES.Util.PEM.toDER(pem),optns);
},
fromDER: function(der) {
},
encode: function(der,sgn) {
return '-----BEGIN ' + sgn + '-----\r\n' + libVES.Util.ByteArrayToB64(der).match(/.{1,64}/g).join("\r\n") + '\r\n-----END ' + sgn + '-----';
}
},
ASN1: {
decode: function(der,fStruct) {
var p = 0;
var data = function() {
var l = der[p++];
var len;
if (l < 128) len = l;
else {
len = 0;
for (var i = 128; i < l; i++) len = (len << 8) | der[p++];
}
if (p + len > der.length) throw new libVES.Error('Internal',"Invalid ASN.1 package");
return der.slice(p,p = p + len);
};
var rs = [];
for (; p < der.length;) {
if (!fStruct && p) {
rs.push(der.slice(p));
break;
}
var tag = der[p++];
switch (tag) {
case 48:
rs.push(libVES.Util.ASN1.decode(data(),true));
break;
case 6:
rs.push(new libVES.Util.OID(data()));
break;
case 2:
var d = data();
var v = 0;
for (var i = 0; i < d.length; i++) v = (v << 8) | d[i];
rs.push(v);
break;
case 5:
data();
rs.push(null);
break;
default:
rs.push(data());
break;
}
}
return rs;
},
encode: function(data,fStruct) {
var i2a = function(v) {
if (v < 0) throw new libVES.Error('Internal',"Negative value for ASN.1 integer!");
var rs = [];
do {
rs.push(v & 0xff);
v >>= 8;
} while (v > 0);
return rs.reverse();
};
var bufs = [];
var buf = function(tag,bf) {
var b = new Uint8Array(bf);
var l = b.length;
var rs;
if (l <= 127) {
rs = new Uint8Array(l + 2);
rs[1] = l;
rs.set(b,2);
} else {
var lb = i2a(l);
rs = new Uint8Array(l + lb.length + 2);
rs[1] = 128 + lb.length;
rs.set(lb,2);
rs.set(b,2 + lb.length);
}
rs[0] = tag;
bufs.push(rs);
return rs;
};
var d;
for (var i = 0; i < data.length; i++) if (fStruct || !i) switch (typeof(d = data[i])) {
case 'object':
if (d == null) buf(5,new Uint8Array(0));
else if (d instanceof Array) buf(48,libVES.Util.ASN1.encode(d,true));
else if (d instanceof libVES.Util.OID) buf(6,d.getBuffer());
else if (d instanceof Uint8Array || d instanceof ArrayBuffer) buf((d.ASN1type || 4),d);
else throw new libVES.Error('Internal',"ASN.1 encode - Unknown type");
break;
case 'number':
buf(2,i2a(d));
break;
default: throw new libVES.Error('Internal',"ASN.1 encode - Unknown type");
} else bufs.push(d);
var l = 0;
for (var i = 0; i < bufs.length; i++) l += bufs[i].length;
var der = new Uint8Array(l);
var p = 0;
for (i = 0, p = 0; i < bufs.length; p += bufs[i].length, i++) der.set(bufs[i],p);
return der;
},
import: function(der,optns) {
var k = optns && optns.decoded || libVES.Util.ASN1.decode(der)[0];
if (!k) throw new libVES.Error('Internal','Empty ASN.1 package?');
var i = 0;
if (typeof(k[i]) == 'number') (optns || (optns = {})).version = k[i++];
if (typeof(k[i]) == 'object' && (k[i][0] instanceof libVES.Util.OID)) return k[i][0].object().then(function(m) {
return m.import(k[i][1],function(call,optns) {
return new Promise(function(resolve,reject) {
switch (call) {
case 'container': return resolve(der);
default: return resolve(k[i + 1]);
}
});
},optns);
});
return libVES.Util.ASN1.import(libVES.Util.ASN1.encode([[0,[new libVES.Util.OID('1.2.840.113549.1.1.1'),null],der]])).catch(function(e) {
throw new libVES.Error('Internal',"Unknown key format",{error: e});
});
},
setType: function(t,buf) {
var rs = new Uint8Array(buf);
rs.ASN1type = t;
return rs;
}
},
OID: function(s) {
if (s instanceof Uint8Array) {
var rs = [ Math.floor(s[0] / 40), s[0] % 40 ];
var r = 0;
for (var p = 1; p < s.length; p++) {
var v = s[p];
r = (r << 7) | (v & 0x7f);
if (!(v & 0x80)) {
rs.push(r);
r = 0;
}
}
this.value = rs.join('.');
} else this.value = s;
},
Key: {
fromDER: function(der, callbk) {
var asn = libVES.Util.ASN1.decode(der)[0];
if (typeof(asn[0]) == 'number') {
var asn2 = libVES.Util.ASN1.decode(asn[2])[0];
var pub = libVES.Util.ASN1.decode(asn2[2]);
console.log('pub', pub);
if (pub) return callbk(pub[0].slice(1), asn2[1], pub[0][0]);
else callbk(null, asn2[1]);
} else return callbk(asn[1].slice(1), null, asn[1][0]);
},
pubASN1: function(pub, optns) {
var p = new Uint8Array(pub.byteLength + 1);
p[0] = optns && optns.pubBits ? optns.pubBits : 0;
p.set(new Uint8Array(pub), 1);
p.ASN1type = 3;
return p;
},
toPKCS: function(pub, priv, asn1hdr, optns) {
if (priv) {
var seq = [1, priv];
if (pub) {
p = libVES.Util.ASN1.encode([libVES.Util.Key.pubASN1(pub, optns)]);
p.ASN1type = 0xa1;
seq.push(p);
}
var buf = libVES.Util.ASN1.encode([seq]);
return libVES.Util.ASN1.encode([[0, asn1hdr, buf]]);
}
return libVES.Util.ASN1.encode([[asn1hdr, libVES.Util.Key.pubASN1(pub, optns)]]);
},
export: function(pub, priv, asn1hdr, optns) {
var pkcs = libVES.Util.Key.toPKCS(pub, priv, asn1hdr, optns);
if (priv) return libVES.Util.PKCS8.encode8(pkcs, optns);
return libVES.Util.PEM.encode(pkcs, 'PUBLIC KEY');
}
},
PKCS1: {
import: function(args,chain,optns) {
return chain('container',optns).then(function(der) {
return crypto.subtle.importKey('spki',der,{name:'RSA-OAEP', hash:'SHA-1'},true,['encrypt']).catch(function(e) {
return crypto.subtle.importKey('pkcs8',der,{name:'RSA-OAEP', hash:'SHA-1'},true,['decrypt']);
});
});
},
encode: function(key,optns) {
return crypto.subtle.exportKey('spki',key).then(function(der) {
return libVES.Util.PEM.encode(der,'PUBLIC KEY');
});
}
},
PKCS5: {
import: function(args,chain,optns) {
var f = chain;
for (var i = args.length - 1; i >= 0; i--) f = (function(obj,fp) {
if (obj[0] instanceof libVES.Util.OID) return function(call,optns) {
return obj[0].object().then(function(m) {
return m[call](obj[1],fp,optns);
});
};
else return fp;
})(args[i],f);
return f('import',optns).then(function(der) {
return libVES.Util.ASN1.import(new Uint8Array(der));
}).catch(function(e) {
throw new libVES.Error('InvalidKey',"Cannot import the private key (Invalid VESkey?)");
});
},
export: function(chain,optns) {
var args = [];
var f = chain;
if (!optns || !(optns.members instanceof Array)) throw new libVES.Error('Internal','PKCS#5: optns.members must be an array');
for (var i = optns.members.length - 1; i >= 0; i--) f = (function(obj,fp,idx) {
return function(call,optns) {
return obj[call](fp,optns).then(function(v) {
if (call == 'export') args[idx] = v;
return v;
});
};
})(optns.members[i],f,i);
return f('export',optns).then(function() {
return [new libVES.Util.OID('1.2.840.113549.1.5.13'), args];
});
}
},
PKCS8: {
encode: function(key, optns) {
return crypto.subtle.exportKey('pkcs8',key).catch(function(e) {
console.log('PKCS8 failed, trying JWK...');
return crypto.subtle.exportKey('jwk', key).then(function(jwk) {
return libVES.Util.PKCS8.fromJWK(jwk);
});
}).then(function(pkcs8) {
return libVES.Util.PKCS8.encode8(pkcs8, optns);
});
},
encode8: function(pkcs8, optns) {
var ops = {};
for (var k in optns) ops[k] = optns[k];
if (!ops.members) ops.members = [
libVES.getModule(libVES.Util,'PBKDF2'),
libVES.getModule(libVES.Cipher,'AES256CBC')
];
return Promise.all(ops.members).then(function(ms) {
ops.members = ms;
ops.content = pkcs8;
var rec = [];
if (ops.password) return libVES.Util.PKCS5.export(function(call,optns) {
rec[1] = optns.content;
return Promise.resolve();
},ops).then(function(data) {
rec[0] = data;
return libVES.Util.PEM.encode(libVES.Util.ASN1.encode([rec]),'ENCRYPTED PRIVATE KEY');
});
else if (ops.opentext) return libVES.Util.PEM.encode(pkcs8,'PRIVATE KEY');
else throw new libVES.Error('Internal','No password for key export (opentext=true to export without password?)');
});
},
fromJWK: function(jwk) {
if (jwk.kty != 'EC') throw new libVES.Error('Internal', 'kty=="EC" expected (workaround for Firefox)');
var pubx = libVES.Util.B64ToByteArray(jwk.x);
var puby = libVES.Util.B64ToByteArray(jwk.y);
var pub = new Uint8Array(pubx.byteLength + puby.byteLength + 2);
pub[0] = 0;
pub[1] = 4;
pub.set(new Uint8Array(pubx), 2);
pub.set(new Uint8Array(puby), pubx.byteLength + 2);
pub.ASN1type = 3;
pub = libVES.Util.ASN1.encode([pub]);
pub.ASN1type = 0xa1;
return libVES.Util.ASN1.encode([[
0,
[
new libVES.Util.OID('1.2.840.10045.2.1'),
new libVES.Util.OID((function(crvs) {
for (var k in crvs) if (crvs[k] == jwk.crv) return k;
throw new libVES.Error('Internal', 'Unknown named curve: ' + jwk.crv);
})(libVES.Util.EC.namedCurves))
],
libVES.Util.ASN1.encode([[
1,
libVES.Util.B64ToByteArray(jwk.d),
pub
]])
]]).buffer;
},
toJWK: function(key) {
var der = libVES.Util.ASN1.decode(key)[0];
var idx = 0;
if (!(der[0] instanceof Array)) idx++;
switch (String(der[idx][0])) {
case '1.2.840.10045.2.1':
case '1.3.132.1.12':
case '1.3.132.112': // Firefox on Mac - apparently a misspelling of the former
break;
default:
console.log(der);
throw new libVES.Error('Internal', 'EC PKCS8 expected');
}
var der2 = idx ? libVES.Util.ASN1.decode(der[idx + 1])[0] : null;
var pub = idx ? (der2[2] ? libVES.Util.ASN1.decode(der2[2])[0] : null) : der[1];
var publ = 0;
if (pub && pub.byteLength > 2 && pub[1] == 4) publ = pub.byteLength / 2 - 1;
return {
crv: libVES.Util.EC.namedCurves[der[idx][1]],
d: (idx ? libVES.Util.ByteArrayToB64W(der2[1]) : undefined),
ext: true,
key_ops: (idx ? ['deriveKey', 'deriveBits'] : []),
kty: 'EC',
x: (publ ? libVES.Util.ByteArrayToB64W(pub.slice(2, publ + 2)) : ''),
y: (publ ? libVES.Util.ByteArrayToB64W(pub.slice(publ + 2, 2 * publ + 2)) : '')
};
}
},
PBKDF2: {
deriveKey: function(args, pwd, algo) {
return crypto.subtle.importKey('raw', libVES.Util.StringToByteArray(pwd), 'PBKDF2', false, ['deriveKey']).then(function(k) {
var keyargs = {name:'PBKDF2', salt:args[0], iterations:args[1]};
var ka;
if (args[2] && (args[2][0] instanceof libVES.Util.OID)) ka = args[2][0].object().then(function(obj) {
obj.setArgs(keyargs, args[2][1]);
return keyargs;
});
else {
keyargs.hash = 'SHA-1';
ka = Promise.resolve(keyargs);
}
return ka.then(function(keyargs) {
return crypto.subtle.deriveKey(keyargs, k, algo, true, ['encrypt', 'decrypt']);
});
});
},
import: function(args,chain,optns) {
if (!optns || !optns.password) throw new libVES.Error('InvalidKey',"VESkey is not supplied");
var pwd = (typeof(optns.password) == '