eslint directives & following corrections

This commit is contained in:
mhelander 2017-09-15 12:45:02 +03:00
parent 61cf9095b6
commit b90640d3dc

126
sea.js
View File

@ -1,4 +1,9 @@
;(function(){
/*eslint max-len: ["error", 95, { "ignoreComments": true }]*/
/*eslint semi: ["error", "always", { "omitLastInOneLineBlock": true}]*/
/*eslint object-curly-spacing: ["error", "never"]*/
/*eslint node/no-deprecated-api: [error, {ignoreModuleItems: ["new buffer.Buffer()"]}] */
;(function(){ // eslint-disable-line no-extra-semi
/*
Security, Encryption, and Authorization: SEA.js
*/
@ -27,7 +32,8 @@
TextDecoder = window.TextDecoder;
localStorage = window.localStorage;
sessionStorage = window.sessionStorage;
indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB
|| window.msIndexedDB || window.shimIndexedDB;
} else {
subtle = require('subtle'); // Web Cryptography API for NodeJS
getRandomBytes = function(len){ return crypto.randomBytes(len) };
@ -38,16 +44,13 @@
sessionStorage = new require('node-localstorage').LocalStorage('session');
indexedDB = undefined; // TODO: simulate IndexedDB in NodeJS but how?
}
console.log('indexedDB:', indexedDB)
// Encryption parameters - TODO: maybe to be changed via init?
var pbkdf2 = {
hash: 'SHA-256', // Was 'SHA-1'
iter: 50000,
ks: 64
};
var ecdh = {
enc: (typeof window !== 'undefined' && 'secp256r1') || 'prime256v1'
};
var aes = {
enc: 'aes-256-cbc'
};
@ -57,7 +60,7 @@ console.log('indexedDB:', indexedDB)
session: true,
hook: function(props){ return props } // { iat, exp, alias, remember }
// or return new Promise(function(resolve, reject){(resolve(props))})
}
};
// These are used to persist user's authentication "session"
var authsettings = {
validity: _initial_authsettings.validity,
@ -81,7 +84,7 @@ console.log('indexedDB:', indexedDB)
user[method] = User[method];
});
return user; // return the user!
}
};
// Practical examples about usage found from ./test/common.js
@ -110,7 +113,7 @@ console.log('indexedDB:', indexedDB)
});
});
return aliases.length && resolve(aliases)
|| reject('Public key does not exist!')
|| reject('Public key does not exist!');
});
});
}
@ -142,7 +145,7 @@ console.log('indexedDB:', indexedDB)
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
// if we were successful, then that meanswe're logged in!
if(priv){
resolve({pub: pub, priv: priv, at: at, proof: proof})
resolve({pub: pub, priv: priv, at: at, proof: proof});
} else if(!remaining){
reject({err: 'Public key does not exist!'});
}
@ -155,7 +158,7 @@ console.log('indexedDB:', indexedDB)
});
}).catch(function(e){ reject({err: e}) });
});
};
}
// This internal func finalizes User authentication
function finalizelogin(alias,key,root,opts){
@ -186,21 +189,21 @@ console.log('indexedDB:', indexedDB)
props.proof = proof;
delete props.remember; // Not stored if present
var remember = (pin && {alias: props.alias, pin: pin }) || props;
var protected = !authsettings.session && pin && props;
var remember = (pin && {alias: props.alias, pin: pin}) || props;
var encrypted = !authsettings.session && pin && props;
return SEA.write(JSON.stringify(remember), priv).then(function(signed){
sessionStorage.setItem('user', props.alias);
sessionStorage.setItem('remember', signed);
if(!protected){
if(!encrypted){
localStorage.removeItem('remember');
}
return !protected || SEA.enc(protected, pin).then(function(encrypted){
return !encrypted || SEA.enc(encrypted, pin).then(function(encrypted){
return encrypted && SEA.write(encrypted, priv).then(function(encsig){
localStorage.setItem('remember', encsig);
}).catch(reject);
}).catch(reject);
}).then(function(){ resolve(props); })
}).then(function(){ resolve(props) })
.catch(function(e){ reject({err: 'Session persisting failed!'}) });
} else {
localStorage.removeItem('remember');
@ -209,7 +212,7 @@ console.log('indexedDB:', indexedDB)
}
resolve(props);
});
}
};
}
// This internal func persists User authentication if so configured
@ -225,7 +228,7 @@ console.log('indexedDB:', indexedDB)
&& new Buffer(opts.pin, 'utf8').toString('base64');
if(proof && user && user.alias && authsettings.validity){
var args = { alias: user.alias };
var args = {alias: user.alias};
args.iat = Math.ceil(Date.now() / 1000); // seconds
args.exp = authsettings.validity; // seconds
if(Gun.obj.has(opts, 'pin')){
@ -239,7 +242,6 @@ console.log('indexedDB:', indexedDB)
}
return updatestorage()({alias: 'delete'});
}
// This internal func recalls persisted User authentication if so configured
function authrecall(root,authprops){
return new Promise(function(resolve, reject){
@ -260,8 +262,7 @@ console.log('indexedDB:', indexedDB)
args.iat = iat;
args.proof = proof;
return args;
} else {
Gun.log('Authentication expired!') }
} else { Gun.log('Authentication expired!') }
};
var hooked = authsettings.hook(decr);
return ((hooked instanceof Promise)
@ -272,7 +273,7 @@ console.log('indexedDB:', indexedDB)
return SEA.read(data, pub).then(function(encrypted){
return SEA.dec(encrypted, key);
}).then(function(decrypted){
try{ return decrypted.slice ? JSON.parse(decrypted) : decrypted }catch(e){}
try{ return decrypted.slice ? JSON.parse(decrypted) : decrypted }catch(e){} //eslint-disable-line no-empty
return decrypted;
});
};
@ -291,13 +292,13 @@ console.log('indexedDB:', indexedDB)
var at = one.at, pub = one.pub;
var remaining = (aliases.length - index) > 1;
if(!at.put){
return !remaining && reject({err: 'Public key does not exist!'})
return !remaining && reject({err: 'Public key does not exist!'});
}
// got pub, time to try auth with alias & PIN...
return ((pin && Promise.resolve({pin: pin, alias: alias}))
// or just unwrap Storage data...
|| SEA.read(remember, pub, true)).then(function(props){
try{ props = props.slice ? JSON.parse(props) : props }catch(e){}
try{ props = props.slice ? JSON.parse(props) : props }catch(e){} //eslint-disable-line no-empty
if(Gun.obj.has(props, 'pin') && Gun.obj.has(props, 'alias')
&& props.alias === alias){
pin = props.pin; // Got PIN so get localStorage secret if signature is ok
@ -328,13 +329,15 @@ console.log('indexedDB:', indexedDB)
: reject({err: 'Failed to decrypt private key!'});
}).catch(function(e){ reject({err: 'Failed to store credentials!'}) });
}).catch(function(e){ reject({err: 'Failed read secret!'}) });
}).catch(function(e){ reject({err: 'Failed to access stored credentials!'}) })
}).catch(function(e){ reject({err: 'Failed to access stored credentials!'}) });
});
});
}).then(function(user){
finalizelogin(alias, user, root).then(resolve).catch(function(e){
Gun.log('Failed to finalize login with new password!');
reject({err: 'Finalizing new password login failed! Reason: '+(e && e.err) || e || ''});
reject({
err: 'Finalizing new password login failed! Reason: '+(e && e.err) || e || ''
});
});
}).catch(function(e){
reject({err: 'No authentication session found!'});
@ -350,7 +353,7 @@ console.log('indexedDB:', indexedDB)
function authleave(root, alias){
return function(resolve, reject){
// remove persisted authentication
user = root._.user;
var user = root._.user;
alias = alias || (user._ && user._.alias);
var doIt = function(){
// TODO: is this correct way to 'logout' user from Gun.User ?
@ -361,7 +364,7 @@ console.log('indexedDB:', indexedDB)
// Let's use default
resolve({ok: 0});
};
authpersist(alias && { alias: alias }).then(doIt).catch(doIt);
authpersist(alias && {alias: alias}).then(doIt).catch(doIt);
};
}
@ -399,7 +402,7 @@ console.log('indexedDB:', indexedDB)
// this will take some short amount of time to produce a proof, which slows brute force attacks.
SEA.pair().then(function(pair){
// now we have generated a brand new ECDSA key pair for the user account.
var user = { pub: pair.pub };
var user = {pub: pair.pub};
var tmp = pair.priv;
// the user's public key doesn't need to be signed. But everything else needs to be signed with it!
SEA.write(alias, tmp).then(function(signedalias){
@ -418,7 +421,7 @@ console.log('indexedDB:', indexedDB)
// awesome, now we can actually save the user with their public key as their ID.
root.get(tmp).put(user);
// next up, we want to associate the alias with the public key. So we add it to the alias list.
var ref = root.get('alias/'+alias).put(Gun.obj.put({}, tmp, Gun.val.rel.ify(tmp)));
root.get('alias/'+alias).put(Gun.obj.put({}, tmp, Gun.val.rel.ify(tmp)));
// callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
resolve({ok: 0, pub: pair.pub});
}).catch(function(e){ Gun.log('SEA.en or SEA.write calls failed!'); reject(e) });
@ -447,7 +450,7 @@ console.log('indexedDB:', indexedDB)
authenticate(alias, pass, root).then(function(key){
// we're logged in!
var pin = Gun.obj.has(opts, 'pin') && { pin: opts.pin };
var pin = Gun.obj.has(opts, 'pin') && {pin: opts.pin};
if(Gun.obj.has(opts, 'newpass')){
// password update so encrypt private key using new pwd + salt
var newsalt = Gun.text.random(64);
@ -474,7 +477,9 @@ console.log('indexedDB:', indexedDB)
// then we're done
finalizelogin(alias, key, root, pin).then(resolve).catch(function(e){
Gun.log('Failed to finalize login with new password!');
reject({err: 'Finalizing new password login failed! Reason: '+(e && e.err) || e || ''});
reject({
err: 'Finalizing new password login failed! Reason: '+(e && e.err) || e || ''
});
});
}).catch(function(e){
Gun.log('Failed encrypt private key using new password!');
@ -499,9 +504,9 @@ console.log('indexedDB:', indexedDB)
if(Gun.is(user)){
user.get('pub').get(function(ctx, ev){
console.log(ctx, ev);
})
});
}
}
};
User.leave = function(cb){
var root = this.back(-1);
if(cb){authleave(root)(cb, cb)} else { return new Promise(authleave(root)) }
@ -516,7 +521,7 @@ console.log('indexedDB:', indexedDB)
// Delete user data
root.get('pub/'+key.pub).put(null);
// Wipe user data from memory
user = root._.user;
var user = root._.user;
// TODO: is this correct way to 'logout' user from Gun.User ?
[ 'alias', 'sea', 'pub' ].forEach(function(key){
delete user._[key];
@ -574,7 +579,7 @@ console.log('indexedDB:', indexedDB)
authrecall(root).then(resolve).catch(function(e){
var err = 'No session!';
Gun.log(err);
resolve({ err: (e && e.err) || err });
resolve({err: (e && e.err) || err});
});
};
if(callback){doIt(callback, callback)} else { return new Promise(doIt) }
@ -584,11 +589,11 @@ console.log('indexedDB:', indexedDB)
var doIt = function(resolve, reject){
authrecall(root).then(function(){
// All is good. Should we do something more with actual recalled data?
resolve(root._.user._)
resolve(root._.user._);
}).catch(function(e){
var err = 'No session!';
Gun.log(err);
reject({ err: err });
reject({err: err});
});
};
if(cb){doIt(cb, cb)} else { return new Promise(doIt) }
@ -621,7 +626,8 @@ console.log('indexedDB:', indexedDB)
// This means we should ONLY trust our "friends" (our key ring) public keys, not any ones.
// I have not yet added that to SEA yet in this alpha release. That is coming soon, but beware in the meanwhile!
function each(at){ // TODO: Warning: Need to switch to `gun.on('node')`! Do not use `Gun.on('node'` in your apps!
var own = (at.gun.back(-1)._).sea.own, soul = at.get, pub = own[soul] || soul.slice(4), vertex = (at.gun._).put;
var own = (at.gun.back(-1)._).sea.own, soul = at.get;
var pub = own[soul] || soul.slice(4), vertex = (at.gun._).put;
Gun.node.is(at.put, function(val, key, node){ // for each property on the node.
SEA.read(val, pub).then(function(data){
vertex[key] = node[key] = val = data; // verify signature and get plain value.
@ -631,7 +637,7 @@ console.log('indexedDB:', indexedDB)
}
});
});
};
}
// signature handles data output, it is a proxy to the security function.
function signature(at){
@ -664,7 +670,7 @@ console.log('indexedDB:', indexedDB)
each.node = function(node, soul){
if(Gun.obj.empty(node, '_')){ return check['node'+soul] = 0 } // ignore empty updates, don't reject them.
Gun.obj.map(node, each.way, {soul: soul, node: node});
}
};
each.way = function(val, key){
var soul = this.soul, node = this.node, tmp;
if('_' === key){ return } // ignore meta data
@ -680,21 +686,21 @@ console.log('indexedDB:', indexedDB)
if(at.user && (tmp = at.user._.sea)){ // not special case, if we are logged in, then
each.user(val, key, node, soul, tmp);
}
if(tmp = sea.own[soul]){ // not special case, if we receive an update on an ID associated with a public key, then
if((tmp = sea.own[soul])){ // not special case, if we receive an update on an ID associated with a public key, then
each.own(val, key, node, soul, tmp);
}
}
};
each.alias = function(val, key, node, soul){
if(!val){ return on.to('end', {err: "Data must exist!"}) } // data MUST exist
if('alias/'+key !== Gun.val.rel.is(val)){ // in fact, it must be EXACTLY equal to itself
return on.to('end', {err: "Mismatching alias."}); // if it isn't, reject.
}
}
};
each.pubs = function(val, key, node, soul){
if(!val){ return on.to('end', {err: "Alias must exist!"}) } // data MUST exist
if(key === Gun.val.rel.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property
return on.to('end', {err: "Alias must match!"}); // that way nobody can tamper with the list of public keys.
}
};
each.pub = function(val, key, node, soul, pub){
//console.log("WE ARE HERE", key, val, soul, node, pub);
if('pub' === key){
@ -715,7 +721,7 @@ console.log('indexedDB:', indexedDB)
}
});
*/
}
};
each.user = function(val, key, node, soul, tmp){
check['user'+soul+key] = 1;
SEA.write(val, tmp, function(data){ // TODO: BUG! Convert to use imported.
@ -723,7 +729,7 @@ console.log('indexedDB:', indexedDB)
check['user'+soul+key] = 0;
on.to('end', {ok: 1});
});
}
};
each.own = function(val, key, node, soul, tmp){
check['own'+soul+key] = 1;
SEA.read(val, tmp, function(data){
@ -733,11 +739,11 @@ console.log('indexedDB:', indexedDB)
// if there is signature, and data is undefined, then:
on.to('end', {no: tmp = (u === (val = data)), err: tmp && "Signature mismatch!"});
});
}
};
on.to('end', function(ctx){ // TODO: Can't you just switch this to each.end = cb?
if(each.err || !each.end){ return }
if(each.err = ctx.err || ctx.no){
console.log("NO!", each.err);
if((each.err = ctx.err) || ctx.no){
console.log('NO!', each.err);
return;
}
if(Gun.obj.map(check, function(no){
@ -750,7 +756,7 @@ console.log('indexedDB:', indexedDB)
return; // need to manually call next after async.
}
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
};
}
// Does enc/dec key like OpenSSL - works with CryptoJS encryption/decryption
function makeKey(p,s){
@ -783,10 +789,12 @@ console.log('indexedDB:', indexedDB)
}).then(resolve).catch(function(e){ Gun.log(e); reject(e) });
}) || function(resolve, reject){ // For NodeJS crypto.pkdf2 rocks
try{
var hash = crypto.pbkdf2Sync(pass,new Buffer(salt, 'utf8'),pbkdf2.iter,pbkdf2.ks,nHash);
var hash = crypto.pbkdf2Sync(
pass, new Buffer(salt, 'utf8'), pbkdf2.iter, pbkdf2.ks, nHash
);
pass = getRandomBytes(pass.length);
resolve(hash && hash.toString('base64'));
}catch(e){ reject(e) };
}catch(e){ reject(e) }
};
if(cb){doIt(cb, function(){cb()})} else { return new Promise(doIt) }
};
@ -821,7 +829,7 @@ console.log('indexedDB:', indexedDB)
var doIt = function(resolve, reject){
ecCrypto.verify(new Buffer(p, 'hex'), nodehash(m), new Buffer(s, 'hex'))
.then(function(){ resolve(true)})
.catch(function(e){ Gun.log(e);reject(e) })
.catch(function(e){ Gun.log(e); reject(e) });
};
if(cb){doIt(cb, function(){cb()})} else { return new Promise(doIt) }
};
@ -857,7 +865,7 @@ console.log('indexedDB:', indexedDB)
};
SEA.dec = function(m,p,cb){
var doIt = function(resolve, reject){
try{ m = m.slice ? JSON.parse(m) : m }catch(e){}
try{ m = m.slice ? JSON.parse(m) : m }catch(e){} //eslint-disable-line no-empty
var key = makeKey(p, new Buffer(m.s, 'hex'));
var iv = new Buffer(m.iv, 'hex');
if(typeof window !== 'undefined'){ // Browser doesn't run createDecipheriv
@ -874,6 +882,7 @@ console.log('indexedDB:', indexedDB)
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
}).catch(function(e){Gun.log(e); reject(e)});
} else { // NodeJS doesn't support subtle.importKey properly
var r;
try{
var decipher = crypto.createDecipheriv(aes.enc, key, iv);
r = decipher.update(m.ct, 'base64', 'utf8') + decipher.final('utf8');
@ -911,10 +920,11 @@ console.log('indexedDB:', indexedDB)
}catch(e){ return reject(e) }
m = m || '';
SEA.verify(m[0], p, m[1]).then(function(ok){
resolve(ok && m[0])
resolve(ok && m[0]);
}).catch(function(e){reject(e)});
};
if(cb && typeof cb === 'function'){doIt(cb, function(){cb()})
if(cb && typeof cb === 'function'){
doIt(cb, function(){cb()});
} else { return new Promise(doIt) }
};
@ -930,5 +940,5 @@ console.log('indexedDB:', indexedDB)
// Adding friends (trusted public keys), sending private messages, etc.
// Cheers! Tell me what you think.
try{module.exports = SEA}catch(e){};
try{module.exports = SEA}catch(e){} //eslint-disable-line no-empty
}());