mirror of
https://github.com/amark/gun.git
synced 2025-07-07 05:12:32 +00:00
manhattan sea
This commit is contained in:
parent
ed495deca6
commit
b4c11f24c7
478
sea.js
478
sea.js
@ -163,6 +163,17 @@
|
|||||||
const api = {Buffer: Buffer}
|
const api = {Buffer: Buffer}
|
||||||
var o = {};
|
var o = {};
|
||||||
|
|
||||||
|
// ideally we can move away from JSON entirely? unlikely due to compatibility issues... oh well.
|
||||||
|
JSON.parseAsync = JSON.parseAsync || function(t,cb,r){ var u; try{ cb(u, JSON.parse(t,r)) }catch(e){ cb(e) } }
|
||||||
|
JSON.stringifyAsync = JSON.stringifyAsync || function(v,cb,r,s){ var u; try{ cb(u, JSON.stringify(v,r,s)) }catch(e){ cb(e) } }
|
||||||
|
|
||||||
|
api.parse = function(t,r){ return new Promise(function(res, rej){
|
||||||
|
JSON.parseAsync(t,function(err, raw){ err? rej(err) : res(raw) },r);
|
||||||
|
})}
|
||||||
|
api.stringify = function(v,r,s){ return new Promise(function(res, rej){
|
||||||
|
JSON.stringifyAsync(v,function(err, raw){ err? rej(err) : res(raw) },r,s);
|
||||||
|
})}
|
||||||
|
|
||||||
if(SEA.window){
|
if(SEA.window){
|
||||||
api.crypto = window.crypto || window.msCrypto
|
api.crypto = window.crypto || window.msCrypto
|
||||||
api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle;
|
api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle;
|
||||||
@ -197,7 +208,7 @@
|
|||||||
|
|
||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
var SEA = USE('./root');
|
var SEA = USE('./root');
|
||||||
var Buffer = USE('./buffer');
|
var shim = USE('./shim');
|
||||||
var s = {};
|
var s = {};
|
||||||
s.pbkdf2 = {hash: {name : 'SHA-256'}, iter: 100000, ks: 64};
|
s.pbkdf2 = {hash: {name : 'SHA-256'}, iter: 100000, ks: 64};
|
||||||
s.ecdsa = {
|
s.ecdsa = {
|
||||||
@ -228,10 +239,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
s.check = function(t){ return (typeof t == 'string') && ('SEA{' === t.slice(0,4)) }
|
s.check = function(t){ return (typeof t == 'string') && ('SEA{' === t.slice(0,4)) }
|
||||||
s.parse = function p(t){ try {
|
s.parse = async function p(t){ try {
|
||||||
var yes = (typeof t == 'string');
|
var yes = (typeof t == 'string');
|
||||||
if(yes && 'SEA{' === t.slice(0,4)){ t = t.slice(3) }
|
if(yes && 'SEA{' === t.slice(0,4)){ t = t.slice(3) }
|
||||||
return yes ? JSON.parse(t) : t;
|
return yes ? await shim.parse(t) : t;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@ -243,7 +254,7 @@
|
|||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
var shim = USE('./shim');
|
var shim = USE('./shim');
|
||||||
module.exports = async function(d, o){
|
module.exports = async function(d, o){
|
||||||
var t = (typeof d == 'string')? d : JSON.stringify(d);
|
var t = (typeof d == 'string')? d : await shim.stringify(d);
|
||||||
var hash = await shim.subtle.digest({name: o||'SHA-256'}, new shim.TextEncoder().encode(t));
|
var hash = await shim.subtle.digest({name: o||'SHA-256'}, new shim.TextEncoder().encode(t));
|
||||||
return shim.Buffer.from(hash);
|
return shim.Buffer.from(hash);
|
||||||
}
|
}
|
||||||
@ -272,7 +283,7 @@
|
|||||||
cb = salt;
|
cb = salt;
|
||||||
salt = u;
|
salt = u;
|
||||||
}
|
}
|
||||||
data = (typeof data == 'string')? data : JSON.stringify(data);
|
data = (typeof data == 'string')? data : await shim.stringify(data);
|
||||||
if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
|
if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
|
||||||
var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64')
|
var rsha = shim.Buffer.from(await sha(data, opt.name), 'binary').toString(opt.encode || 'base64')
|
||||||
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||||
@ -388,12 +399,12 @@
|
|||||||
pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
|
pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
|
||||||
}
|
}
|
||||||
if(u === data){ throw '`undefined` not allowed.' }
|
if(u === data){ throw '`undefined` not allowed.' }
|
||||||
var json = S.parse(data);
|
var json = await S.parse(data);
|
||||||
var check = opt.check = opt.check || json;
|
var check = opt.check = opt.check || json;
|
||||||
if(SEA.verify && (SEA.opt.check(check) || (check && check.s && check.m))
|
if(SEA.verify && (SEA.opt.check(check) || (check && check.s && check.m))
|
||||||
&& u !== await SEA.verify(check, pair)){ // don't sign if we already signed it.
|
&& u !== await SEA.verify(check, pair)){ // don't sign if we already signed it.
|
||||||
var r = S.parse(check);
|
var r = await S.parse(check);
|
||||||
if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
|
if(!opt.raw){ r = 'SEA' + await shim.stringify(r) }
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -404,7 +415,7 @@
|
|||||||
var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['sign'])
|
var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['sign'])
|
||||||
.then((key) => (shim.ossl || shim.subtle).sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
.then((key) => (shim.ossl || shim.subtle).sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||||
var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')}
|
var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')}
|
||||||
if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
|
if(!opt.raw){ r = 'SEA' + await shim.stringify(r) }
|
||||||
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
@ -427,9 +438,9 @@
|
|||||||
var u;
|
var u;
|
||||||
|
|
||||||
SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try {
|
SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try {
|
||||||
var json = S.parse(data);
|
var json = await S.parse(data);
|
||||||
if(false === pair){ // don't verify!
|
if(false === pair){ // don't verify!
|
||||||
var raw = S.parse(json.m);
|
var raw = await S.parse(json.m);
|
||||||
if(cb){ try{ cb(raw) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(raw) }catch(e){console.log(e)} }
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
@ -448,7 +459,7 @@
|
|||||||
return await SEA.opt.fall_verify(data, pair, cb, opt);
|
return await SEA.opt.fall_verify(data, pair, cb, opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var r = check? S.parse(json.m) : u;
|
var r = check? await S.parse(json.m) : u;
|
||||||
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
@ -476,8 +487,8 @@
|
|||||||
if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
|
if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
|
||||||
var tmp = data||'';
|
var tmp = data||'';
|
||||||
data = SEA.opt.unpack(data) || data;
|
data = SEA.opt.unpack(data) || data;
|
||||||
var json = S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
|
var json = await S.parse(data), pub = pair.pub || pair, key = await SEA.opt.slow_leak(pub);
|
||||||
var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
|
var hash = (f <= SEA.opt.fallback)? shim.Buffer.from(await shim.subtle.digest({name: 'SHA-256'}, new shim.TextEncoder().encode(await S.parse(json.m)))) : await sha(json.m); // this line is old bad buggy code but necessary for old compatibility.
|
||||||
var buf; var sig; var check; try{
|
var buf; var sig; var check; try{
|
||||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||||
sig = new Uint8Array(buf)
|
sig = new Uint8Array(buf)
|
||||||
@ -489,7 +500,7 @@
|
|||||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||||
if(!check){ throw "Signature did not match." }
|
if(!check){ throw "Signature did not match." }
|
||||||
}
|
}
|
||||||
var r = check? S.parse(json.m) : u;
|
var r = check? await S.parse(json.m) : u;
|
||||||
O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
|
O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
@ -530,7 +541,7 @@
|
|||||||
pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why});
|
pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why});
|
||||||
key = pair.epriv || pair;
|
key = pair.epriv || pair;
|
||||||
}
|
}
|
||||||
var msg = (typeof data == 'string')? data : JSON.stringify(data);
|
var msg = (typeof data == 'string')? data : await shim.stringify(data);
|
||||||
var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding.
|
var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding.
|
||||||
var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
|
var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
|
||||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
||||||
@ -540,7 +551,7 @@
|
|||||||
iv: rand.iv.toString(opt.encode || 'base64'),
|
iv: rand.iv.toString(opt.encode || 'base64'),
|
||||||
s: rand.s.toString(opt.encode || 'base64')
|
s: rand.s.toString(opt.encode || 'base64')
|
||||||
}
|
}
|
||||||
if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
|
if(!opt.raw){ r = 'SEA' + await shim.stringify(r) }
|
||||||
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
@ -568,7 +579,7 @@
|
|||||||
pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why});
|
pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why});
|
||||||
key = pair.epriv || pair;
|
key = pair.epriv || pair;
|
||||||
}
|
}
|
||||||
var json = S.parse(data);
|
var json = await S.parse(data);
|
||||||
var buf, bufiv, bufct; try{
|
var buf, bufiv, bufct; try{
|
||||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64');
|
buf = shim.Buffer.from(json.s, opt.encode || 'base64');
|
||||||
bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64');
|
bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64');
|
||||||
@ -583,7 +594,7 @@
|
|||||||
return await SEA.decrypt(data, pair, cb, opt);
|
return await SEA.decrypt(data, pair, cb, opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var r = S.parse(new shim.TextDecoder('utf8').decode(ct));
|
var r = await S.parse(new shim.TextDecoder('utf8').decode(ct));
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
@ -701,27 +712,21 @@
|
|||||||
// But all other behavior needs to be equally easy, like opinionated ways of
|
// But all other behavior needs to be equally easy, like opinionated ways of
|
||||||
// Adding friends (trusted public keys), sending private messages, etc.
|
// Adding friends (trusted public keys), sending private messages, etc.
|
||||||
// Cheers! Tell me what you think.
|
// Cheers! Tell me what you think.
|
||||||
var Gun = (SEA.window||{}).Gun || USE((typeof MODULE == "undefined"?'.':'')+'./gun', 1);
|
((SEA.window||{}).GUN||{}).SEA = SEA;
|
||||||
Gun.SEA = SEA;
|
|
||||||
SEA.GUN = SEA.Gun = Gun;
|
|
||||||
|
|
||||||
module.exports = SEA
|
module.exports = SEA
|
||||||
|
// -------------- END SEA MODULES --------------------
|
||||||
|
// -- BEGIN SEA+GUN MODULES: BUNDLED BY DEFAULT UNTIL OTHERS USE SEA ON OWN -------
|
||||||
})(USE, './sea');
|
})(USE, './sea');
|
||||||
|
|
||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
var Gun = USE('./sea').Gun;
|
var SEA = USE('./sea'), Gun, u;
|
||||||
Gun.chain.then = function(cb, opt){
|
if(SEA.window){
|
||||||
var gun = this, p = (new Promise(function(res, rej){
|
Gun = SEA.window.GUN || {chain:{}};
|
||||||
gun.once(res, opt);
|
} else {
|
||||||
}));
|
Gun = USE((typeof MODULE == "undefined"?'.':'')+'./gun', 1);
|
||||||
return cb? p.then(cb) : p;
|
|
||||||
}
|
}
|
||||||
})(USE, './then');
|
SEA.GUN = Gun;
|
||||||
|
|
||||||
;USE(function(module){
|
|
||||||
var SEA = USE('./sea');
|
|
||||||
var Gun = SEA.Gun;
|
|
||||||
var then = USE('./then');
|
|
||||||
|
|
||||||
function User(root){
|
function User(root){
|
||||||
this._ = {$: this};
|
this._ = {$: this};
|
||||||
@ -735,7 +740,7 @@
|
|||||||
var gun = this, root = gun.back(-1), user;
|
var gun = this, root = gun.back(-1), user;
|
||||||
if(pub){ return root.get('~'+pub) }
|
if(pub){ return root.get('~'+pub) }
|
||||||
if(user = root.back('user')){ return user }
|
if(user = root.back('user')){ return user }
|
||||||
var root = (root._), at = root, uuid = at.opt.uuid || Gun.state.lex;
|
var root = (root._), at = root, uuid = at.opt.uuid || lex;
|
||||||
(at = (user = at.user = gun.chain(new User))._).opt = {};
|
(at = (user = at.user = gun.chain(new User))._).opt = {};
|
||||||
at.opt.uuid = function(cb){
|
at.opt.uuid = function(cb){
|
||||||
var id = uuid(), pub = root.user;
|
var id = uuid(), pub = root.user;
|
||||||
@ -746,20 +751,25 @@
|
|||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
function lex(){ return Gun.state().toString(36).replace('.','') }
|
||||||
Gun.User = User;
|
Gun.User = User;
|
||||||
|
User.GUN = Gun;
|
||||||
|
User.SEA = Gun.SEA = SEA;
|
||||||
module.exports = User;
|
module.exports = User;
|
||||||
})(USE, './user');
|
})(USE, './user');
|
||||||
|
|
||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
// TODO: This needs to be split into all separate functions.
|
var u, Gun = (''+u != typeof window)? (window.Gun||{chain:{}}) : require('../gun');
|
||||||
// Not just everything thrown into 'create'.
|
Gun.chain.then = function(cb, opt){
|
||||||
|
var gun = this, p = (new Promise(function(res, rej){
|
||||||
|
gun.once(res, opt);
|
||||||
|
}));
|
||||||
|
return cb? p.then(cb) : p;
|
||||||
|
}
|
||||||
|
})(USE, './then');
|
||||||
|
|
||||||
var SEA = USE('./sea');
|
;USE(function(module){
|
||||||
var User = USE('./user');
|
var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){};
|
||||||
var authsettings = USE('./settings');
|
|
||||||
var Gun = SEA.Gun;
|
|
||||||
|
|
||||||
var noop = function(){};
|
|
||||||
|
|
||||||
// Well first we have to actually create a user. That is what this function does.
|
// Well first we have to actually create a user. That is what this function does.
|
||||||
User.prototype.create = function(alias, pass, cb, opt){
|
User.prototype.create = function(alias, pass, cb, opt){
|
||||||
@ -782,7 +792,7 @@
|
|||||||
gun.leave();
|
gun.leave();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
act.salt = Gun.text.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it.
|
act.salt = String.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it.
|
||||||
SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks.
|
SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks.
|
||||||
}
|
}
|
||||||
act.b = function(proof){
|
act.b = function(proof){
|
||||||
@ -813,19 +823,44 @@
|
|||||||
}
|
}
|
||||||
act.g = function(auth){ var tmp;
|
act.g = function(auth){ var tmp;
|
||||||
act.data.auth = act.data.auth || auth;
|
act.data.auth = act.data.auth || auth;
|
||||||
root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID.
|
root.get(tmp = '~'+act.pair.pub).put(act.data).on(act.h); // awesome, now we can actually save the user with their public key as their ID.
|
||||||
root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list.
|
var link = {}; link[tmp] = {'#': tmp}; root.get('~@'+alias).put(link).get(tmp).on(act.i); // next up, we want to associate the alias with the public key. So we add it to the alias list.
|
||||||
setTimeout(function(){ // we should be able to delete this now, right?
|
}
|
||||||
|
act.h = function(data, key, msg, eve){
|
||||||
|
eve.off(); act.h.ok = 1; act.i();
|
||||||
|
}
|
||||||
|
act.i = function(data, key, msg, eve){
|
||||||
|
if(eve){ act.i.ok = 1; eve.off() }
|
||||||
|
if(!act.h.ok || !act.i.ok){ return }
|
||||||
cat.ing = false;
|
cat.ing = false;
|
||||||
cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
|
cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
|
||||||
if(noop === cb){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
|
if(noop === cb){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
|
||||||
},10);
|
|
||||||
}
|
}
|
||||||
root.get('~@'+alias).once(act.a);
|
root.get('~@'+alias).once(act.a);
|
||||||
return gun;
|
return gun;
|
||||||
}
|
}
|
||||||
|
User.prototype.leave = function(opt, cb){
|
||||||
|
var gun = this, user = (gun.back(-1)._).user;
|
||||||
|
if(user){
|
||||||
|
delete user.is;
|
||||||
|
delete user._.is;
|
||||||
|
delete user._.sea;
|
||||||
|
}
|
||||||
|
if(SEA.window){
|
||||||
|
try{var sS = {};
|
||||||
|
sS = window.sessionStorage;
|
||||||
|
delete sS.recall;
|
||||||
|
delete sS.pair;
|
||||||
|
}catch(e){};
|
||||||
|
}
|
||||||
|
return gun;
|
||||||
|
}
|
||||||
|
})(USE, './create');
|
||||||
|
|
||||||
|
;USE(function(module){
|
||||||
|
var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){};
|
||||||
// now that we have created a user, we want to authenticate them!
|
// now that we have created a user, we want to authenticate them!
|
||||||
User.prototype.auth = function(){
|
User.prototype.auth = function(){ // TODO: this PR with arguments need to be cleaned up / refactored.
|
||||||
const alias = typeof arguments[0] === 'string' ? arguments[0] : null
|
const alias = typeof arguments[0] === 'string' ? arguments[0] : null
|
||||||
const pass = alias && typeof arguments[1] === 'string' ? arguments[1] : null
|
const pass = alias && typeof arguments[1] === 'string' ? arguments[1] : null
|
||||||
const pair = typeof arguments[0] === 'object' && (arguments[0].pub || arguments[0].epub) ? arguments[0] : typeof arguments[1] === 'object' && (arguments[1].pub || arguments[1].epub) ? arguments[1] : null
|
const pair = typeof arguments[0] === 'object' && (arguments[0].pub || arguments[0].epub) ? arguments[0] : typeof arguments[1] === 'object' && (arguments[1].pub || arguments[1].epub) ? arguments[1] : null
|
||||||
@ -844,8 +879,7 @@
|
|||||||
act.a = function(data){
|
act.a = function(data){
|
||||||
if(!data){ return act.b() }
|
if(!data){ return act.b() }
|
||||||
if(!data.pub){
|
if(!data.pub){
|
||||||
var tmp = [];
|
var tmp = []; Object.keys(data).forEach(function(k){ if('_'==k){ return } tmp.push(data[k]) })
|
||||||
Gun.node.is(data, function(v){ tmp.push(v) })
|
|
||||||
return act.b(tmp);
|
return act.b(tmp);
|
||||||
}
|
}
|
||||||
if(act.name){ return act.f(data) }
|
if(act.name){ return act.f(data) }
|
||||||
@ -861,7 +895,7 @@
|
|||||||
}
|
}
|
||||||
act.c = function(auth){
|
act.c = function(auth){
|
||||||
if(u === auth){ return act.b() }
|
if(u === auth){ return act.b() }
|
||||||
if(Gun.text.is(auth)){ return act.c(Gun.obj.ify(auth)) } // in case of legacy
|
if('string' == typeof auth){ return act.c(obj_ify(auth)) } // in case of legacy
|
||||||
SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
SEA.work(pass, (act.auth = auth).s, act.d, act.enc); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||||
}
|
}
|
||||||
act.d = function(proof){
|
act.d = function(proof){
|
||||||
@ -878,12 +912,12 @@
|
|||||||
act.half = half;
|
act.half = half;
|
||||||
act.f(act.data);
|
act.f(act.data);
|
||||||
}
|
}
|
||||||
act.f = function(data){
|
act.f = function(pair){
|
||||||
if(!data || !data.pub){ return act.b() }
|
var half = act.half || {}, data = act.data || {};
|
||||||
var tmp = act.half || {};
|
act.g(act.lol = {pub: pair.pub || data.pub, epub: pair.epub || data.epub, priv: pair.priv || half.priv, epriv: pair.epriv || half.epriv});
|
||||||
act.g({pub: data.pub, epub: data.epub, priv: tmp.priv, epriv: tmp.epriv});
|
|
||||||
}
|
}
|
||||||
act.g = function(pair){
|
act.g = function(pair){
|
||||||
|
if(!pair || !pair.pub || !pair.epub){ return act.b() }
|
||||||
act.pair = pair;
|
act.pair = pair;
|
||||||
var user = (root._).user, at = (user._);
|
var user = (root._).user, at = (user._);
|
||||||
var tmp = at.tag;
|
var tmp = at.tag;
|
||||||
@ -894,7 +928,7 @@
|
|||||||
user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
||||||
at.sea = act.pair;
|
at.sea = act.pair;
|
||||||
cat.ing = false;
|
cat.ing = false;
|
||||||
try{if(pass && !Gun.obj.has(Gun.obj.ify(cat.root.graph['~'+pair.pub].auth), ':')){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle!
|
try{if(pass && u == (obj_ify(cat.root.graph['~'+pair.pub].auth)||'')[':']){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle!
|
||||||
opt.change? act.z() : cb(at);
|
opt.change? act.z() : cb(at);
|
||||||
if(SEA.window && ((gun.back('user')._).opt||opt).remember){
|
if(SEA.window && ((gun.back('user')._).opt||opt).remember){
|
||||||
// TODO: this needs to be modular.
|
// TODO: this needs to be modular.
|
||||||
@ -913,7 +947,7 @@
|
|||||||
}
|
}
|
||||||
act.z = function(){
|
act.z = function(){
|
||||||
// password update so encrypt private key using new pwd + salt
|
// password update so encrypt private key using new pwd + salt
|
||||||
act.salt = Gun.text.random(64); // pseudo-random
|
act.salt = String.random(64); // pseudo-random
|
||||||
SEA.work(opt.change, act.salt, act.y);
|
SEA.work(opt.change, act.salt, act.y);
|
||||||
}
|
}
|
||||||
act.y = function(proof){
|
act.y = function(proof){
|
||||||
@ -925,8 +959,8 @@
|
|||||||
act.w = function(auth){
|
act.w = function(auth){
|
||||||
if(opt.shuffle){ // delete in future!
|
if(opt.shuffle){ // delete in future!
|
||||||
console.log('migrate core account from UTF8 & shuffle');
|
console.log('migrate core account from UTF8 & shuffle');
|
||||||
var tmp = Gun.obj.to(act.data);
|
var tmp = {}; Object.keys(act.data).forEach(function(k){ tmp[k] = act.data[k] });
|
||||||
Gun.obj.del(tmp, '_');
|
delete tmp._;
|
||||||
tmp.auth = auth;
|
tmp.auth = auth;
|
||||||
root.get('~'+act.pair.pub).put(tmp);
|
root.get('~'+act.pair.pub).put(tmp);
|
||||||
} // end delete
|
} // end delete
|
||||||
@ -957,46 +991,16 @@
|
|||||||
}
|
}
|
||||||
return gun;
|
return gun;
|
||||||
}
|
}
|
||||||
User.prototype.pair = function(){
|
function obj_ify(o){
|
||||||
console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!");
|
if('string' != typeof o){ return o }
|
||||||
var user = this;
|
try{o = JSON.parse(o);
|
||||||
if(!user.is){ return false }
|
}catch(e){o={}};
|
||||||
return user._.sea;
|
return o;
|
||||||
}
|
|
||||||
User.prototype.leave = function(opt, cb){
|
|
||||||
var gun = this, user = (gun.back(-1)._).user;
|
|
||||||
if(user){
|
|
||||||
delete user.is;
|
|
||||||
delete user._.is;
|
|
||||||
delete user._.sea;
|
|
||||||
}
|
|
||||||
if(SEA.window){
|
|
||||||
try{var sS = {};
|
|
||||||
sS = window.sessionStorage;
|
|
||||||
delete sS.recall;
|
|
||||||
delete sS.pair;
|
|
||||||
}catch(e){};
|
|
||||||
}
|
|
||||||
return gun;
|
|
||||||
}
|
|
||||||
// If authenticated user wants to delete his/her account, let's support it!
|
|
||||||
User.prototype.delete = async function(alias, pass, cb){
|
|
||||||
console.log("user.delete() IS DEPRECATED AND WILL BE MOVED TO A MODULE!!!");
|
|
||||||
var gun = this, root = gun.back(-1), user = gun.back('user');
|
|
||||||
try {
|
|
||||||
user.auth(alias, pass, function(ack){
|
|
||||||
var pub = (user.is||{}).pub;
|
|
||||||
// Delete user data
|
|
||||||
user.map().once(function(){ this.put(null) });
|
|
||||||
// Wipe user data from memory
|
|
||||||
user.leave();
|
|
||||||
(cb || noop)({ok: 0});
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
Gun.log('User.delete failed! Error:', e);
|
|
||||||
}
|
|
||||||
return gun;
|
|
||||||
}
|
}
|
||||||
|
})(USE, './auth');
|
||||||
|
|
||||||
|
;USE(function(module){
|
||||||
|
var User = USE('./user'), SEA = User.SEA, Gun = User.GUN;
|
||||||
User.prototype.recall = function(opt, cb){
|
User.prototype.recall = function(opt, cb){
|
||||||
var gun = this, root = gun.back(-1);
|
var gun = this, root = gun.back(-1);
|
||||||
opt = opt || {};
|
opt = opt || {};
|
||||||
@ -1022,6 +1026,34 @@
|
|||||||
*/
|
*/
|
||||||
return gun;
|
return gun;
|
||||||
}
|
}
|
||||||
|
})(USE, './recall');
|
||||||
|
|
||||||
|
;USE(function(module){
|
||||||
|
var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){};
|
||||||
|
User.prototype.pair = function(){
|
||||||
|
console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!");
|
||||||
|
var user = this;
|
||||||
|
if(!user.is){ return false }
|
||||||
|
return user._.sea;
|
||||||
|
}
|
||||||
|
// If authenticated user wants to delete his/her account, let's support it!
|
||||||
|
User.prototype.delete = async function(alias, pass, cb){
|
||||||
|
console.log("user.delete() IS DEPRECATED AND WILL BE MOVED TO A MODULE!!!");
|
||||||
|
var gun = this, root = gun.back(-1), user = gun.back('user');
|
||||||
|
try {
|
||||||
|
user.auth(alias, pass, function(ack){
|
||||||
|
var pub = (user.is||{}).pub;
|
||||||
|
// Delete user data
|
||||||
|
user.map().once(function(){ this.put(null) });
|
||||||
|
// Wipe user data from memory
|
||||||
|
user.leave();
|
||||||
|
(cb || noop)({ok: 0});
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
Gun.log('User.delete failed! Error:', e);
|
||||||
|
}
|
||||||
|
return gun;
|
||||||
|
}
|
||||||
User.prototype.alive = async function(){
|
User.prototype.alive = async function(){
|
||||||
console.log("user.alive() IS DEPRECATED!!!");
|
console.log("user.alive() IS DEPRECATED!!!");
|
||||||
const gunRoot = this.back(-1)
|
const gunRoot = this.back(-1)
|
||||||
@ -1036,6 +1068,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
User.prototype.trust = async function(user){
|
User.prototype.trust = async function(user){
|
||||||
|
console.log("`.trust` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
|
||||||
// TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too.
|
// TODO: BUG!!! SEA `node` read listener needs to be async, which means core needs to be async too.
|
||||||
//gun.get('alice').get('age').trust(bob);
|
//gun.get('alice').get('age').trust(bob);
|
||||||
if (Gun.is(user)) {
|
if (Gun.is(user)) {
|
||||||
@ -1128,25 +1161,24 @@
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
module.exports = User
|
module.exports = User
|
||||||
})(USE, './create');
|
})(USE, './share');
|
||||||
|
|
||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
var SEA = USE('./sea')
|
var SEA = USE('./sea'), noop = function(){}, u;
|
||||||
var Gun = SEA.Gun;
|
var Gun = (''+u != typeof window)? (window.Gun||{on:noop}) : require('../gun');
|
||||||
// After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
|
// After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
|
||||||
|
|
||||||
// We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change)
|
// We do this with a GUN adapter, we first listen to when a gun instance is created (and when its options change)
|
||||||
Gun.on('opt', function(at){
|
Gun.on('opt', function(at){
|
||||||
if(!at.sea){ // only add SEA once per instance, on the "at" context.
|
if(!at.sea){ // only add SEA once per instance, on the "at" context.
|
||||||
at.sea = {own: {}};
|
at.sea = {own: {}};
|
||||||
//at.on('in', security, at); // now listen to all input data, acting as a firewall.
|
at.on('put', check, at); // SEA now runs its firewall on HAM diffs, not all i/o.
|
||||||
//at.on('out', signature, at); // and output listeners, to encrypt outgoing data.
|
|
||||||
at.on('put', check, at);
|
|
||||||
}
|
}
|
||||||
this.to.next(at); // make sure to call the "next" middleware adapter.
|
this.to.next(at); // make sure to call the "next" middleware adapter.
|
||||||
});
|
});
|
||||||
|
|
||||||
// Alright, this next adapter gets run at the per node level in the graph database.
|
// Alright, this next adapter gets run at the per node level in the graph database.
|
||||||
|
// correction: 2020 it gets run on each key/value pair in a node upon a HAM diff.
|
||||||
// This will let us verify that every property on a node has a value signed by a public key we trust.
|
// This will let us verify that every property on a node has a value signed by a public key we trust.
|
||||||
// If the signature does not match, the data is just `undefined` so it doesn't get passed on.
|
// If the signature does not match, the data is just `undefined` so it doesn't get passed on.
|
||||||
// If it does match, then we transform the in-memory "view" of the data into its plain value (without the signature).
|
// If it does match, then we transform the in-memory "view" of the data into its plain value (without the signature).
|
||||||
@ -1159,46 +1191,17 @@
|
|||||||
// Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous!
|
// Here is a problem: Multiple public keys can "claim" any node's ID, so this is dangerous!
|
||||||
// This means we should ONLY trust our "friends" (our key ring) public keys, not any ones.
|
// 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!
|
// I have not yet added that to SEA yet in this alpha release. That is coming soon, but beware in the meanwhile!
|
||||||
function each(msg){ // TODO: Warning: Need to switch to `gun.on('node')`! Do not use `Gun.on('node'` in your apps!
|
|
||||||
// NOTE: THE SECURITY FUNCTION HAS ALREADY VERIFIED THE DATA!!!
|
|
||||||
// WE DO NOT NEED TO RE-VERIFY AGAIN, JUST TRANSFORM IT TO PLAINTEXT.
|
|
||||||
var to = this.to, vertex = (msg.$._).put, c = 0, d;
|
|
||||||
Gun.node.is(msg.put, function(val, key, node){
|
|
||||||
// only process if SEA formatted?
|
|
||||||
var tmp = Gun.obj.ify(val) || noop;
|
|
||||||
if(u !== tmp[':']){
|
|
||||||
node[key] = SEA.opt.unpack(tmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!SEA.opt.check(val)){ return }
|
|
||||||
c++; // for each property on the node
|
|
||||||
SEA.verify(val, false, function(data){ c--; // false just extracts the plain data.
|
|
||||||
node[key] = SEA.opt.unpack(data, key, node);; // transform to plain value.
|
|
||||||
if(d && !c && (c = -1)){ to.next(msg) }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if((d = true) && !c){ to.next(msg) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// signature handles data output, it is a proxy to the security function.
|
|
||||||
function signature(msg){
|
|
||||||
if((msg._||noop).user){
|
|
||||||
return this.to.next(msg);
|
|
||||||
}
|
|
||||||
var ctx = this.as;
|
|
||||||
(msg._||(msg._=function(){})).user = ctx.user;
|
|
||||||
security.call(this, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var u;
|
|
||||||
function check(msg){ // REVISE / IMPROVE, NO NEED TO PASS MSG/EVE EACH SUB?
|
function check(msg){ // REVISE / IMPROVE, NO NEED TO PASS MSG/EVE EACH SUB?
|
||||||
var eve = this, at = eve.as, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
|
var eve = this, at = eve.as, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
|
||||||
if(!soul || !key){ return }
|
if(!soul || !key){ return }
|
||||||
|
//console.log('check', put, msg);
|
||||||
if((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){
|
if((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){
|
||||||
SEA.verify(SEA.opt.pack(put), false, function(data){ // this is synchronous if false
|
SEA.opt.pack(put, function(raw){
|
||||||
|
SEA.verify(raw, false, function(data){ // this is synchronous if false
|
||||||
put['='] = SEA.opt.unpack(data);
|
put['='] = SEA.opt.unpack(data);
|
||||||
eve.to.next(msg);
|
eve.to.next(msg);
|
||||||
});
|
})})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var no = function(why){ at.on('in', {'@': id, err: why}) };
|
var no = function(why){ at.on('in', {'@': id, err: why}) };
|
||||||
@ -1208,7 +1211,7 @@
|
|||||||
// 'a~pub.key/b<?9'
|
// 'a~pub.key/b<?9'
|
||||||
tmp = parseFloat(soul.split('<?')[1]||'');
|
tmp = parseFloat(soul.split('<?')[1]||'');
|
||||||
if(tmp && (state < (Gun.state() - (tmp * 1000)))){ // sec to ms
|
if(tmp && (state < (Gun.state() - (tmp * 1000)))){ // sec to ms
|
||||||
(tmp = msg._) && (tmp = tmp.lot) && (tmp.more--); // THIS IS BAD CODE! It assumes GUN internals do something that will probably change in future, but hacking in now.
|
(tmp = msg._) && (tmp.stun) && (tmp.stun--); // THIS IS BAD CODE! It assumes GUN internals do something that will probably change in future, but hacking in now.
|
||||||
return; // omit!
|
return; // omit!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1249,23 +1252,29 @@
|
|||||||
if(val === pub){ return eve.to.next(msg) } // the account MUST match `pub` property that equals the ID of the public key.
|
if(val === pub){ return eve.to.next(msg) } // the account MUST match `pub` property that equals the ID of the public key.
|
||||||
return no("Account not same!");
|
return no("Account not same!");
|
||||||
}
|
}
|
||||||
if((tmp = user.is) && pub === tmp.pub){
|
if((tmp = user.is) && pub === tmp.pub){// && (tmp = msg._.$) && (tmp = tmp._) && tmp !== tmp.root){
|
||||||
SEA.sign(SEA.opt.pack(msg.put), (user._).sea, function(data){
|
SEA.opt.pack(msg.put, function(raw){
|
||||||
|
SEA.sign(raw, (user._).sea, function(data){
|
||||||
if(u === data){ return no(SEA.err || 'Signature fail.') }
|
if(u === data){ return no(SEA.err || 'Signature fail.') }
|
||||||
if(tmp = link_is(val)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
if(tmp = link_is(val)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
||||||
msg.put[':'] = JSON.stringify({':': tmp = SEA.opt.unpack(data.m), '~': data.s});
|
//console.log("SYNCHRONOUS JSON!!! CHECK EVERYWHERE ELSE");
|
||||||
msg.put['='] = tmp;
|
JSON.stringifyAsync({':': tmp = SEA.opt.unpack(data.m), '~': data.s}, function(err,s){
|
||||||
eve.to.next(msg);
|
if(err){ return no(err || "Stringify error.") }
|
||||||
}, {raw: 1});
|
msg.put['='] = tmp;
|
||||||
|
msg.put[':'] = s;
|
||||||
|
eve.to.next(msg);
|
||||||
|
})
|
||||||
|
}, {raw: 1})});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SEA.verify(SEA.opt.pack(msg.put), pub, function(data){ var tmp;
|
SEA.opt.pack(msg.put, function(raw){
|
||||||
|
SEA.verify(raw, pub, function(data){ var tmp;
|
||||||
data = SEA.opt.unpack(data);
|
data = SEA.opt.unpack(data);
|
||||||
if(u === data){ return no("Unverified data.") } // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
|
if(u === data){ return no("Unverified data.") } // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
|
||||||
if((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
if((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
||||||
msg.put['='] = data;
|
msg.put['='] = data;
|
||||||
eve.to.next(msg);
|
eve.to.next(msg);
|
||||||
});
|
})});
|
||||||
};
|
};
|
||||||
check.any = function(eve, msg, val, key, soul, at, no, user){ var tmp, pub;
|
check.any = function(eve, msg, val, key, soul, at, no, user){ var tmp, pub;
|
||||||
if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
|
if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
|
||||||
@ -1276,162 +1285,9 @@
|
|||||||
}).on.on('secure', msg);
|
}).on.on('secure', msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var valid = Gun.valid, link_is = function(d,l){ return 'string' == typeof (l = valid(d)) && l }, state_ify = Gun.state.ify;
|
|
||||||
|
|
||||||
// okay! The security function handles all the heavy lifting.
|
var valid = Gun.valid, link_is = function(d,l){ return 'string' == typeof (l = valid(d)) && l }, state_ify = (Gun.state||'').ify;
|
||||||
// It needs to deal read and write of input and output of system data, account/public key data, and regular data.
|
|
||||||
// This is broken down into some pretty clear edge cases, let's go over them:
|
|
||||||
function security(msg){
|
|
||||||
var at = this.as, sea = at.sea, to = this.to;
|
|
||||||
if(at.opt.faith && (msg._||noop).faith){ // you probably shouldn't have faith in this!
|
|
||||||
this.to.next(msg); // why do we allow skipping security? I'm very scared about it actually.
|
|
||||||
return; // but so that way storage adapters that already verified something can get performance boost. This was a community requested feature. If anybody finds an exploit with it, please report immediately. It should only be exploitable if you have XSS control anyways, which if you do, you can bypass security regardless of this.
|
|
||||||
}
|
|
||||||
if(msg.get){
|
|
||||||
// if there is a request to read data from us, then...
|
|
||||||
var soul = msg.get['#'];
|
|
||||||
if(soul){ // for now, only allow direct IDs to be read.
|
|
||||||
if(typeof soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors.
|
|
||||||
if('alias' === soul){ // Allow reading the list of usernames/aliases in the system?
|
|
||||||
return to.next(msg); // yes.
|
|
||||||
} else
|
|
||||||
if('~@' === soul.slice(0,2)){ // Allow reading the list of public keys associated with an alias?
|
|
||||||
return to.next(msg); // yes.
|
|
||||||
} else { // Allow reading everything?
|
|
||||||
return to.next(msg); // yes // TODO: No! Make this a callback/event that people can filter on.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(msg.put){
|
|
||||||
/*
|
|
||||||
NOTICE: THIS IS OLD AND GETTING DEPRECATED.
|
|
||||||
ANY SECURITY CHANGES SHOULD HAPPEN ABOVE FIRST
|
|
||||||
THEN PORTED TO HERE.
|
|
||||||
*/
|
|
||||||
// potentially parallel async operations!!!
|
|
||||||
var check = {}, each = {}, u;
|
|
||||||
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
|
|
||||||
if('~@' === soul){ // special case for shared system data, the list of aliases.
|
|
||||||
each.alias(val, key, node, soul); return;
|
|
||||||
}
|
|
||||||
if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias.
|
|
||||||
each.pubs(val, key, node, soul); return;
|
|
||||||
}
|
|
||||||
if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
|
|
||||||
each.pub(val, key, node, soul, tmp, (msg._||noop).user); return;
|
|
||||||
}
|
|
||||||
each.any(val, key, node, soul, (msg._||noop).user); return;
|
|
||||||
return each.end({err: "No other data allowed!"});
|
|
||||||
};
|
|
||||||
each.alias = function(val, key, node, soul){ // Example: {_:#~@, ~@alice: {#~@alice}}
|
|
||||||
if(!val){ return each.end({err: "Data must exist!"}) } // data MUST exist
|
|
||||||
if('~@'+key === Gun.val.link.is(val)){ return check['alias'+key] = 0 } // in fact, it must be EXACTLY equal to itself
|
|
||||||
each.end({err: "Mismatching alias."}); // if it isn't, reject.
|
|
||||||
};
|
|
||||||
each.pubs = function(val, key, node, soul){ // Example: {_:#~@alice, ~asdf: {#~asdf}}
|
|
||||||
if(!val){ return each.end({err: "Alias must exist!"}) } // data MUST exist
|
|
||||||
if(key === Gun.val.link.is(val)){ return check['pubs'+soul+key] = 0 } // and the ID must be EXACTLY equal to its property
|
|
||||||
each.end({err: "Alias must match!"}); // that way nobody can tamper with the list of public keys.
|
|
||||||
};
|
|
||||||
each.pub = function(val, key, node, soul, pub, user){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
|
|
||||||
if('pub' === key){
|
|
||||||
if(val === pub){ return (check['pub'+soul+key] = 0) } // the account MUST match `pub` property that equals the ID of the public key.
|
|
||||||
return each.end({err: "Account must match!"});
|
|
||||||
}
|
|
||||||
check['user'+soul+key] = 1;
|
|
||||||
if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){
|
|
||||||
SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (user._).sea, function(data){ var rel;
|
|
||||||
if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
|
|
||||||
if(rel = Gun.val.link.is(val)){
|
|
||||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
|
||||||
}
|
|
||||||
node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
|
|
||||||
check['user'+soul+key] = 0;
|
|
||||||
each.end({ok: 1});
|
|
||||||
}, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel, tmp;
|
|
||||||
data = SEA.opt.unpack(data, key, node);
|
|
||||||
if(u === data){ // make sure the signature matches the account it claims to be on.
|
|
||||||
return each.end({err: "Unverified data."}); // reject any updates that are signed with a mismatched account.
|
|
||||||
}
|
|
||||||
if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){
|
|
||||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
|
||||||
}
|
|
||||||
check['user'+soul+key] = 0;
|
|
||||||
each.end({ok: 1});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
each.any = function(val, key, node, soul, user){ var tmp, pub;
|
|
||||||
if(!(pub = SEA.opt.pub(soul))){
|
|
||||||
if(at.opt.secure){
|
|
||||||
each.end({err: "Soul is missing public key at '" + key + "'."});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: Ask community if should auto-sign non user-graph data.
|
|
||||||
check['any'+soul+key] = 1;
|
|
||||||
at.on('secure', function(msg){ this.off();
|
|
||||||
check['any'+soul+key] = 0;
|
|
||||||
if(at.opt.secure){ msg = null }
|
|
||||||
each.end(msg || {err: "Data cannot be modified."});
|
|
||||||
}).on.on('secure', msg);
|
|
||||||
//each.end({err: "Data cannot be modified."});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(Gun.is(msg.$) && user && user.is && pub === user.is.pub){
|
|
||||||
/*var other = Gun.obj.map(at.sea.own[soul], function(v, p){
|
|
||||||
if((user.is||{}).pub !== p){ return p }
|
|
||||||
});
|
|
||||||
if(other){
|
|
||||||
each.any(val, key, node, soul);
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
check['any'+soul+key] = 1;
|
|
||||||
SEA.sign(SEA.opt.prep(tmp = SEA.opt.parse(val), key, node, soul), (user._).sea, function(data){
|
|
||||||
if(u === data){ return each.end({err: 'My signature fail.'}) }
|
|
||||||
node[key] = JSON.stringify({':': SEA.opt.unpack(data.m), '~': data.s});
|
|
||||||
check['any'+soul+key] = 0;
|
|
||||||
each.end({ok: 1});
|
|
||||||
}, {check: SEA.opt.pack(tmp, key, node, soul), raw: 1});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
check['any'+soul+key] = 1;
|
|
||||||
SEA.verify(SEA.opt.pack(val,key,node,soul), pub, function(data){ var rel;
|
|
||||||
data = SEA.opt.unpack(data, key, node);
|
|
||||||
if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
|
|
||||||
if((rel = Gun.val.link.is(data)) && pub === SEA.opt.pub(rel)){
|
|
||||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
|
||||||
}
|
|
||||||
check['any'+soul+key] = 0;
|
|
||||||
each.end({ok: 1});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
each.end = function(ctx){ // TODO: Can't you just switch this to each.end = cb?
|
|
||||||
if(each.err){ return }
|
|
||||||
if((each.err = ctx.err) || ctx.no){
|
|
||||||
console.log('NO!', each.err, msg.put); // 451 mistmached data FOR MARTTI
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!each.end.ed){ return }
|
|
||||||
if(Gun.obj.map(check, function(no){
|
|
||||||
if(no){ return true }
|
|
||||||
})){ return }
|
|
||||||
(msg._||{}).user = at.user || security; // already been through firewall, does not need to again on out.
|
|
||||||
to.next(msg);
|
|
||||||
};
|
|
||||||
Gun.obj.map(msg.put, each.node);
|
|
||||||
each.end({end: each.end.ed = true});
|
|
||||||
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).
|
|
||||||
}
|
|
||||||
var pubcut = /[^\w_-]/; // anything not alphanumeric or _ -
|
var pubcut = /[^\w_-]/; // anything not alphanumeric or _ -
|
||||||
SEA.opt.pub = function(s){
|
SEA.opt.pub = function(s){
|
||||||
if(!s){ return }
|
if(!s){ return }
|
||||||
@ -1443,13 +1299,14 @@
|
|||||||
s = s.slice(0,2).join('.');
|
s = s.slice(0,2).join('.');
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
SEA.opt.prep = function(d,k, n,s){ // prep for signing
|
SEA.opt.pack = function(d,cb,k, n,s){ var tmp, f; // pack for verifying
|
||||||
return {'#':s,'.':k,':':SEA.opt.parse(d),'>':Gun.state.is(n, k)};
|
if(SEA.opt.check(d)){ return cb(d) }
|
||||||
}
|
if(d && d['#'] && d['.'] && d['>']){ tmp = d[':']; f = 1 }
|
||||||
SEA.opt.pack = function(d,k, n,s){ // pack for verifying
|
JSON.parseAsync(f? tmp : d, function(err, meta){
|
||||||
if(SEA.opt.check(d)){ return d }
|
var sig = ((u !== (meta||'')[':']) && (meta||'')['~']); // or just ~ check?
|
||||||
var meta = (Gun.obj.ify((d && d[':'])||d)||''), sig = meta['~'];
|
if(!sig){ cb(d); return }
|
||||||
return sig? {m: {'#':s||d['#'],'.':k||d['.'],':':meta[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig} : d;
|
cb({m: {'#':s||d['#'],'.':k||d['.'],':':(meta||'')[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
var O = SEA.opt;
|
var O = SEA.opt;
|
||||||
SEA.opt.unpack = function(d, k, n){ var tmp;
|
SEA.opt.unpack = function(d, k, n){ var tmp;
|
||||||
@ -1459,7 +1316,7 @@
|
|||||||
if(!k || !n){ return }
|
if(!k || !n){ return }
|
||||||
if(d === n[k]){ return d }
|
if(d === n[k]){ return d }
|
||||||
if(!SEA.opt.check(n[k])){ return d }
|
if(!SEA.opt.check(n[k])){ return d }
|
||||||
var soul = Gun.node.soul(n) || O.fall_soul, s = Gun.state.is(n, k) || O.fall_state;
|
var soul = (n && n._ && n._['#']) || O.fall_soul, s = Gun.state.is(n, k) || O.fall_state;
|
||||||
if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
|
if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){
|
||||||
return d[2];
|
return d[2];
|
||||||
}
|
}
|
||||||
@ -1468,7 +1325,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
|
SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019
|
||||||
var noop = function(){}, u;
|
|
||||||
var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
|
var fl = Math.floor; // TODO: Still need to fix inconsistent state issue.
|
||||||
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
|
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user