ソースを参照

update gundb, WebRTC off by default

Nikolay Suslov 4 年 前
コミット
3c6a3401dd

+ 2 - 4
public/app.js

@@ -109,14 +109,12 @@ class App {
 
     this.config = config;
 
-    const opt = { peers: this.dbHost, localStorage: false, axe: false }
+    const opt = { peers: this.dbHost, localStorage: false, RTCPeerConnection: false, axe: false }
     //const opt = { peers: this.dbHost, localStorage: false, until: 1000, chunk: 5, axe: false} //until: 5000, chunk: 5
     //opt.store = RindexedDB(opt);
     this.db = Gun(opt);
-    this.q =
 
-
-      this.user = this.db.user();
+    this.user = this.db.user();
     window._LCSDB = this.db;
     window._LCSUSER = this.user;
     window._LCS_SYS_USER = undefined;

ファイルの差分が大きいため隠しています
+ 0 - 0
public/lib/gundb/gun.min.js


+ 11 - 0
public/lib/gundb/lib/nomem.js

@@ -0,0 +1,11 @@
+function Nomem(){
+  var opt = {}, u;
+  opt.put = function(file, data, cb){ cb(null, -9) }; // dev/null!
+  opt.get = function(file, cb){ cb(null) };
+  return opt;
+}
+if(typeof window !== "undefined"){
+  window.Nomem = Nomem;
+} else {
+	try{ module.exports = Nomem }catch(e){}
+}

+ 18 - 22
public/lib/gundb/lib/wire.js

@@ -51,36 +51,32 @@ var Gun = require('../gun');
 
 Gun.on('opt', function(root){
 	var opt = root.opt;
-	if(false === opt.ws){
+	if(false === opt.ws || opt.once){
 		this.to.next(root);
 		return;
 	}	
 
 	var url = require('url');
+	opt.mesh = opt.mesh || Gun.Mesh(root);
 	opt.WebSocket = opt.WebSocket || require('ws');
 	var ws = opt.ws = opt.ws || {};
-	ws.server = ws.server || opt.web;
-
-	if(ws.server && !ws.web){
-
-		opt.mesh = opt.mesh || Gun.Mesh(root);
-		ws.path = ws.path || '/gun';
-		ws.maxPayload = ws.maxPayload; // || opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3;
-		ws.web = new opt.WebSocket.Server(ws);
-		ws.web.on('connection', function(wire){ var peer;
-			wire.upgradeReq = wire.upgradeReq || {};
-			wire.url = url.parse(wire.upgradeReq.url||'', true);
-			opt.mesh.hi(peer = {wire: wire});
-			wire.on('message', function(msg){
-				opt.mesh.hear(msg.data || msg, peer);
-			});
-			wire.on('close', function(){
-				opt.mesh.bye(peer);
-			});
-			wire.on('error', function(e){});
-			setTimeout(function heart(){ if(!opt.peers[peer.id]){ return } try{ wire.send("[]") }catch(e){} ;setTimeout(heart, 1000 * 20) }, 1000 * 20); // Some systems, like Heroku, require heartbeats to not time out. // TODO: Make this configurable? // TODO: PERF: Find better approach than try/timeouts?
+	ws.path = ws.path || '/gun';
+	// if we DO need an HTTP server, then choose ws specific one or GUN default one.
+	if(!ws.noServer){ ws.server = ws.server || opt.web }
+	ws.web = ws.web || new opt.WebSocket.Server(ws); // we still need a WS server.
+	ws.web.on('connection', function(wire){ var peer;
+		wire.upgradeReq = wire.upgradeReq || {};
+		wire.url = url.parse(wire.upgradeReq.url||'', true);
+		opt.mesh.hi(peer = {wire: wire});
+		wire.on('message', function(msg){
+			opt.mesh.hear(msg.data || msg, peer);
+		});
+		wire.on('close', function(){
+			opt.mesh.bye(peer);
 		});
-	}
+		wire.on('error', function(e){});
+		setTimeout(function heart(){ if(!opt.peers[peer.id]){ return } try{ wire.send("[]") }catch(e){} ;setTimeout(heart, 1000 * 20) }, 1000 * 20); // Some systems, like Heroku, require heartbeats to not time out. // TODO: Make this configurable? // TODO: PERF: Find better approach than try/timeouts?
+	});
 
 	this.to.next(root);
 });

+ 3 - 3
public/lib/gundb/sea/base64.js

@@ -1,9 +1,9 @@
 
     if(typeof btoa === "undefined"){
       if(typeof Buffer === "undefined") {
-        root.Buffer = require("buffer").Buffer
+        global.Buffer = require("buffer").Buffer
       }
-      root.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
-      root.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
+      global.btoa = function (data) { return Buffer.from(data, "binary").toString("base64"); };
+      global.atob = function (data) { return Buffer.from(data, "base64").toString("binary"); };
     }
   

+ 13 - 4
public/lib/gundb/sea/create.js

@@ -226,6 +226,7 @@
     }
     // 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){
@@ -267,6 +268,7 @@
       return gun;
     }
     User.prototype.alive = async function(){
+      console.log("user.alive() IS DEPRECATED!!!");
       const gunRoot = this.back(-1)
       try {
         // All is good. Should we do something more with actual recalled data?
@@ -286,25 +288,32 @@
           console.log(ctx, ev)
         })
       }
+      user.get('trust').get(path).put(theirPubkey);
+
+      // do a lookup on this gun chain directly (that gets bob's copy of the data)
+      // do a lookup on the metadata trust table for this path (that gets all the pubkeys allowed to write on this path)
+      // do a lookup on each of those pubKeys ON the path (to get the collab data "layers")
+      // THEN you perform Jachen's mix operation
+      // and return the result of that to...
     }
     User.prototype.grant = function(to, cb){
       console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
-      var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
+      var gun = this, user = gun.back(-1).user(), pair = user._.sea, path = '';
       gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
       (async function(){
-      var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
+      var enc, sec = await user.get('grant').get(pair.pub).get(path).then();
       sec = await SEA.decrypt(sec, pair);
       if(!sec){
         sec = SEA.random(16).toString();
         enc = await SEA.encrypt(sec, pair);
-        user.get('trust').get(pair.pub).get(path).put(enc);
+        user.get('grant').get(pair.pub).get(path).put(enc);
       }
       var pub = to.get('pub').then();
       var epub = to.get('epub').then();
       pub = await pub; epub = await epub;
       var dh = await SEA.secret(epub, pair);
       enc = await SEA.encrypt(sec, dh);
-      user.get('trust').get(pub).get(path).put(enc, cb);
+      user.get('grant').get(pub).get(path).put(enc, cb);
       }());
       return gun;
     }

+ 108 - 10
public/lib/gundb/sea/index.js

@@ -1,15 +1,15 @@
 
-    const SEA = require('./sea')
-    const Gun = SEA.Gun;
+    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('node', each, at);
+        //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.
     });
@@ -58,6 +58,94 @@
       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('<?')){ // special case for "do not sync data X old"
+        // 'a~pub.key/b<?9'
+        tmp = parseFloat(soul.split('<?')[1]||'');
+        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.
+          return; // omit!
+        }
+      }
+      if('~@' === soul){  // special case for shared system data, the list of aliases.
+        check.alias(eve, msg, val, key, soul, at, no); return;
+      }
+      if('~@' === soul.slice(0,2)){ // special case for shared system data, the list of public keys for an alias.
+        check.pubs(eve, msg, val, key, soul, at, no); return;
+      }
+      //if('~' === soul.slice(0,1) && 2 === (tmp = soul.slice(1)).split('.').length){ // special case, account data for a public key.
+      if(tmp = SEA.opt.pub(soul)){ // special case, account data for a public key.
+        check.pub(eve, msg, val, key, soul, at, no, at.user||'', tmp); return;
+      }
+      if(0 <= soul.indexOf('#')){ // special case for content addressing immutable hashed data.
+        check.hash(eve, msg, val, key, soul, at, no); return;
+      } 
+      check.any(eve, msg, val, key, soul, at, no, at.user||''); return;
+      eve.to.next(msg); // not handled
+    }
+    check.hash = function(eve, msg, val, key, soul, at, no){
+      SEA.work(val, null, function(data){
+        if(data && data === key.split('#').slice(-1)[0]){ return eve.to.next(msg) }
+        no("Data hash not same as hash!");
+      }, {name: 'SHA-256'});
+    }
+    check.alias = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@, ~@alice: {#~@alice}}
+      if(!val){ return no("Data must exist!") } // data MUST exist
+      if('~@'+key === link_is(val)){ return eve.to.next(msg) } // in fact, it must be EXACTLY equal to itself
+      no("Alias not same!"); // if it isn't, reject.
+    };
+    check.pubs = function(eve, msg, val, key, soul, at, no){ // Example: {_:#~@alice, ~asdf: {#~asdf}}
+      if(!val){ return no("Alias must exist!") } // data MUST exist
+      if(key === link_is(val)){ return eve.to.next(msg) } // and the ID must be EXACTLY equal to its property
+      no("Alias not same!"); // that way nobody can tamper with the list of public keys.
+    };
+    check.pub = function(eve, msg, val, key, soul, at, no, user, pub){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
+      if('pub' === key && '~'+pub === soul){
+        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!");
+      }
+      if((tmp = user.is) && pub === tmp.pub){
+        SEA.sign(SEA.opt.pack(msg.put), (user._).sea, function(data){
+          if(u === data){ return no(SEA.err || 'Signature fail.') }
+          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});
+          msg.put['='] = tmp;
+          eve.to.next(msg);
+        }, {raw: 1});
+        return;
+      }
+      SEA.verify(SEA.opt.pack(msg.put), pub, function(data){ var tmp;
+        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((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
+        msg.put['='] = data;
+        eve.to.next(msg);
+      });
+    };
+    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 + "'.") }
+      // TODO: Ask community if should auto-sign non user-graph data.
+      at.on('secure', function(msg){ this.off();
+        if(!at.opt.secure){ return eve.to.next(msg) }
+        no("Data cannot be changed.");
+      }).on.on('secure', msg);
+      return;
+    }
+    var link_is = Gun.val.link.is, state_ify = Gun.state.ify;
+
     // okay! The security function handles all the heavy lifting.
     // 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:
@@ -83,6 +171,11 @@
         }
       }
       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){
@@ -207,12 +300,14 @@
       }
       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 _ -
     SEA.opt.pub = function(s){
       if(!s){ return }
       s = s.split('~');
       if(!s || !(s = s[1])){ return }
-      s = s.split('.');
-      if(!s || 2 > s.length){ return }
+      s = s.split(pubcut).slice(0,2);
+      if(!s || 2 != s.length){ return }
+      if('@' === (s[0]||'')[0]){ return }
       s = s.slice(0,2).join('.');
       return s;
     }
@@ -221,16 +316,18 @@
     }
     SEA.opt.pack = function(d,k, n,s){ // pack for verifying
       if(SEA.opt.check(d)){ return d }
-      var meta = (Gun.obj.ify(d)||noop), sig = meta['~'];
-      return sig? {m: {'#':s,'.':k,':':meta[':'],'>':Gun.state.is(n, k)}, s: sig} : 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), s = Gun.state.is(n, k);
+      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];
       }
@@ -242,6 +339,7 @@
     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.
 
   

+ 1 - 1
public/lib/gundb/sea/root.js

@@ -11,6 +11,6 @@
 
     if(SEA.window = module.window){ SEA.window.SEA = SEA }
 
-    try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
+    try{ if(typeof MODULE !== "undefined"){ MODULE.exports = SEA } }catch(e){}
     module.exports = SEA;
   

+ 3 - 3
public/lib/gundb/sea/sea.js

@@ -1,13 +1,13 @@
 
     var shim = require('./shim');
-    // Practical examples about usage found from ./test/common.js
+    // Practical examples about usage found in tests.
     var SEA = require('./root');
     SEA.work = require('./work');
     SEA.sign = require('./sign');
     SEA.verify = require('./verify');
     SEA.encrypt = require('./encrypt');
     SEA.decrypt = require('./decrypt');
-    SEA.aeskey = require('./aeskey');
+    SEA.opt.aeskey = require('./aeskey'); // not official!
 
     SEA.random = SEA.random || shim.random;
 
@@ -49,7 +49,7 @@
     // But all other behavior needs to be equally easy, like opinionated ways of
     // Adding friends (trusted public keys), sending private messages, etc.
     // Cheers! Tell me what you think.
-    var Gun = (SEA.window||{}).Gun || require((typeof common == "undefined"?'.':'')+'./gun', 1);
+    var Gun = (SEA.window||{}).Gun || require((typeof MODULE == "undefined"?'.':'')+'./gun', 1);
     Gun.SEA = SEA;
     SEA.GUN = SEA.Gun = Gun;
 

+ 2 - 2
public/lib/gundb/sea/shim.js

@@ -26,8 +26,8 @@
       const isocrypto = require('isomorphic-webcrypto');
       api.ossl = api.subtle = isocrypto.subtle;
     }catch(e){
-      console.log("node-webcrypto-ossl and text-encoding may not be included by default, please add it to your package.json!");
-      OSSL_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
+      console.log("text-encoding and @peculiar/webcrypto may not be included by default, please add it to your package.json!");
+      TEXT_ENCODING_OR_PECULIAR_WEBCRYPTO_NOT_INSTALLED;
     }}
 
     module.exports = api

+ 1 - 1
public/lib/gundb/sea/user.js

@@ -20,7 +20,7 @@
       at.opt.uuid = function(cb){
         var id = uuid(), pub = root.user;
         if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id }
-        id = id + '~' + pub + '.';
+        id = id + '~' + pub + '/';
         if(cb && cb.call){ cb(null, id) }
         return id;
       }

+ 5 - 2
public/lib/gundb/sea/verify.js

@@ -15,7 +15,7 @@
       opt = opt || {};
       // SEA.I // verify is free! Requires no user permission.
       var pub = pair.pub || pair;
-      var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
+      var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', S.jwk(pub), {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
       var hash = await sha(json.m);
       var buf, sig, check, tmp; try{
         buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
@@ -50,9 +50,11 @@
       return knownKeys[pair];
     };
 
-
+    var O = SEA.opt;
     SEA.opt.fall_verify = async function(data, pair, cb, opt, f){
       if(f === SEA.opt.fallback){ throw "Signature did not match" } f = f || 1;
+      var tmp = data||'';
+      data = SEA.opt.unpack(data) || data;
       var json = 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 buf; var sig; var check; try{
@@ -67,6 +69,7 @@
         if(!check){ throw "Signature did not match." }
       }
       var r = check? S.parse(json.m) : u;
+      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)} }
       return r;
     }

+ 1 - 1
public/lib/gundb/sea/work.js

@@ -12,13 +12,13 @@
         cb = salt;
         salt = u;
       }
-      salt = salt || shim.random(9);
       data = (typeof data == 'string')? data : JSON.stringify(data);
       if('sha' === (opt.name||'').toLowerCase().slice(0,3)){
         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)} }
         return rsha;
       }
+      salt = salt || shim.random(9);
       var key = await (shim.ossl || shim.subtle).importKey('raw', new shim.TextEncoder().encode(data), {name: opt.name || 'PBKDF2'}, false, ['deriveBits']);
       var work = await (shim.ossl || shim.subtle).deriveBits({
         name: opt.name || 'PBKDF2',

+ 2 - 2
public/loadLibs.js

@@ -16,7 +16,7 @@ loadjs([
 });
 
 
-loadjs(['/lib/gundb/gun.js',
+loadjs(['/lib/gundb/gun.min.js',
         '/lib/gundb/sea.js',
         '/lib/gundb/lib/then.js',
         '/lib/gundb/lib/path.js',
@@ -26,7 +26,7 @@ loadjs(['/lib/gundb/gun.js',
         '/lib/gundb/lib/promise.js',
         '/lib/gundb/lib/time.js',
         '/lib/gundb/lib/bye.js',
-        '/lib/gundb/lib/webrtc.js',
+       '/lib/gundb/lib/webrtc.js',
         '/lib/gundb/nts.js',
         '/lib/gundb/lib/radix.js',
         '/lib/gundb/lib/radisk.js',

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません