var SEA = require('./sea') var Gun = SEA.Gun; // 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) Gun.on('opt', function(at){ if(!at.sea){ // only add SEA once per instance, on the "at" context. at.sea = {own: {}}; //at.on('in', security, at); // now listen to all input data, acting as a firewall. //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. }); // Alright, this next adapter gets run at the per node level in the graph database. // 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 it does match, then we transform the in-memory "view" of the data into its plain value (without the signature). // Now NOTE! Some data is "system" data, not user data. Example: List of public keys, aliases, etc. // This data is self-enforced (the value can only match its ID), but that is handled in the `security` function. // From the self-enforced data, we can see all the edges in the graph that belong to a public key. // Example: ~ASDF is the ID of a node with ASDF as its public key, signed alias and salt, and // its encrypted private key, but it might also have other signed values on it like `profile = ` edge. // Using that directed edge's ID, we can then track (in memory) which IDs belong to which keys. // 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. // 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? 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((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){ SEA.verify(SEA.opt.pack(put), false, function(data){ // this is synchronous if false put['='] = SEA.opt.unpack(data); eve.to.next(msg); }); return } var no = function(why){ at.on('in', {'@': id, err: why}) }; //var no = function(why){ msg.ack(why) }; (msg._||'').DBG && ((msg._||'').DBG.c = +new Date); if(0 <= soul.indexOf('':Gun.state.is(n, k)}; } SEA.opt.pack = function(d,k, n,s){ // pack for verifying if(SEA.opt.check(d)){ return d } var meta = (Gun.obj.ify((d && d[':'])||d)||''), sig = meta['~']; return sig? {m: {'#':s||d['#'],'.':k||d['.'],':':meta[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig} : d; } var O = SEA.opt; SEA.opt.unpack = function(d, k, n){ var tmp; if(u === d){ return } if(d && (u !== (tmp = d[':']))){ return tmp } k = k || O.fall_key; if(!n && O.fall_val){ n = {}; n[k] = O.fall_val } if(!k || !n){ return } if(d === 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; if(d && 4 === d.length && soul === d[0] && k === d[1] && fl(s) === fl(d[3])){ return d[2]; } if(s < SEA.opt.shuffle_attack){ return d; } } 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 rel_is = Gun.val.rel.is; var obj_ify = Gun.obj.ify; // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.