From b4c11f24c786752e82ef2b3d22dc545d83cdb5da Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 18 Sep 2020 20:54:33 -0700 Subject: [PATCH] manhattan sea --- sea.js | 480 ++++++++++++++++++++------------------------------------- 1 file changed, 168 insertions(+), 312 deletions(-) diff --git a/sea.js b/sea.js index 8765ec5d..1e99fc00 100644 --- a/sea.js +++ b/sea.js @@ -163,6 +163,17 @@ const api = {Buffer: Buffer} 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){ api.crypto = window.crypto || window.msCrypto api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle; @@ -197,7 +208,7 @@ ;USE(function(module){ var SEA = USE('./root'); - var Buffer = USE('./buffer'); + var shim = USE('./shim'); var s = {}; s.pbkdf2 = {hash: {name : 'SHA-256'}, iter: 100000, ks: 64}; s.ecdsa = { @@ -228,10 +239,10 @@ }; 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'); 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) {} return t; } @@ -243,7 +254,7 @@ ;USE(function(module){ var shim = USE('./shim'); 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)); return shim.Buffer.from(hash); } @@ -272,7 +283,7 @@ cb = salt; 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)){ 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)} } @@ -388,12 +399,12 @@ pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why}); } 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; 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. - var r = S.parse(check); - if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } + var r = await S.parse(check); + if(!opt.raw){ r = 'SEA' + await shim.stringify(r) } if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; } @@ -404,7 +415,7 @@ 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! 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)} } return r; @@ -427,9 +438,9 @@ var u; 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! - var raw = S.parse(json.m); + var raw = await S.parse(json.m); if(cb){ try{ cb(raw) }catch(e){console.log(e)} } return raw; } @@ -448,7 +459,7 @@ 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)} } return r; @@ -476,8 +487,8 @@ 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 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(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{ buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT! 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)) 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['>']; if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; @@ -530,7 +541,7 @@ pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why}); 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 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) @@ -540,7 +551,7 @@ iv: rand.iv.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)} } return r; @@ -568,7 +579,7 @@ pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why}); key = pair.epriv || pair; } - var json = S.parse(data); + var json = await S.parse(data); var buf, bufiv, bufct; try{ buf = shim.Buffer.from(json.s, opt.encode || 'base64'); bufiv = shim.Buffer.from(json.iv, opt.encode || 'base64'); @@ -583,7 +594,7 @@ 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)} } return r; } catch(e) { @@ -701,27 +712,21 @@ // 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 || USE((typeof MODULE == "undefined"?'.':'')+'./gun', 1); - Gun.SEA = SEA; - SEA.GUN = SEA.Gun = Gun; + ((SEA.window||{}).GUN||{}).SEA = SEA; module.exports = SEA + // -------------- END SEA MODULES -------------------- + // -- BEGIN SEA+GUN MODULES: BUNDLED BY DEFAULT UNTIL OTHERS USE SEA ON OWN ------- })(USE, './sea'); ;USE(function(module){ - var Gun = USE('./sea').Gun; - 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; + var SEA = USE('./sea'), Gun, u; + if(SEA.window){ + Gun = SEA.window.GUN || {chain:{}}; + } else { + Gun = USE((typeof MODULE == "undefined"?'.':'')+'./gun', 1); } - })(USE, './then'); - - ;USE(function(module){ - var SEA = USE('./sea'); - var Gun = SEA.Gun; - var then = USE('./then'); + SEA.GUN = Gun; function User(root){ this._ = {$: this}; @@ -735,7 +740,7 @@ var gun = this, root = gun.back(-1), user; if(pub){ return root.get('~'+pub) } 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.opt.uuid = function(cb){ var id = uuid(), pub = root.user; @@ -746,20 +751,25 @@ } return user; } + function lex(){ return Gun.state().toString(36).replace('.','') } Gun.User = User; + User.GUN = Gun; + User.SEA = Gun.SEA = SEA; module.exports = User; })(USE, './user'); ;USE(function(module){ - // TODO: This needs to be split into all separate functions. - // Not just everything thrown into 'create'. + var u, Gun = (''+u != typeof window)? (window.Gun||{chain:{}}) : require('../gun'); + 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'); - var User = USE('./user'); - var authsettings = USE('./settings'); - var Gun = SEA.Gun; - - var noop = function(){}; + ;USE(function(module){ + var User = USE('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){}; // Well first we have to actually create a user. That is what this function does. User.prototype.create = function(alias, pass, cb, opt){ @@ -782,7 +792,7 @@ gun.leave(); 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. } act.b = function(proof){ @@ -813,19 +823,44 @@ } act.g = function(auth){ var tmp; 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('~@'+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. - setTimeout(function(){ // we should be able to delete this now, right? + 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. + 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. + } + 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; 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. - },10); } root.get('~@'+alias).once(act.a); 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! - 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 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 @@ -844,8 +879,7 @@ act.a = function(data){ if(!data){ return act.b() } if(!data.pub){ - var tmp = []; - Gun.node.is(data, function(v){ tmp.push(v) }) + var tmp = []; Object.keys(data).forEach(function(k){ if('_'==k){ return } tmp.push(data[k]) }) return act.b(tmp); } if(act.name){ return act.f(data) } @@ -861,7 +895,7 @@ } act.c = function(auth){ 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. } act.d = function(proof){ @@ -878,12 +912,12 @@ act.half = half; act.f(act.data); } - act.f = function(data){ - if(!data || !data.pub){ return act.b() } - var tmp = act.half || {}; - act.g({pub: data.pub, epub: data.epub, priv: tmp.priv, epriv: tmp.epriv}); + act.f = function(pair){ + var half = act.half || {}, data = act.data || {}; + 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 = function(pair){ + if(!pair || !pair.pub || !pair.epub){ return act.b() } act.pair = pair; var user = (root._).user, at = (user._); var tmp = at.tag; @@ -894,7 +928,7 @@ user.is = {pub: pair.pub, epub: pair.epub, alias: alias}; at.sea = act.pair; 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); if(SEA.window && ((gun.back('user')._).opt||opt).remember){ // TODO: this needs to be modular. @@ -913,7 +947,7 @@ } act.z = function(){ // 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); } act.y = function(proof){ @@ -925,8 +959,8 @@ act.w = function(auth){ if(opt.shuffle){ // delete in future! console.log('migrate core account from UTF8 & shuffle'); - var tmp = Gun.obj.to(act.data); - Gun.obj.del(tmp, '_'); + var tmp = {}; Object.keys(act.data).forEach(function(k){ tmp[k] = act.data[k] }); + delete tmp._; tmp.auth = auth; root.get('~'+act.pair.pub).put(tmp); } // end delete @@ -957,46 +991,16 @@ } return gun; } - 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; - } - 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; + function obj_ify(o){ + if('string' != typeof o){ return o } + try{o = JSON.parse(o); + }catch(e){o={}}; + return o; } + })(USE, './auth'); + + ;USE(function(module){ + var User = USE('./user'), SEA = User.SEA, Gun = User.GUN; User.prototype.recall = function(opt, cb){ var gun = this, root = gun.back(-1); opt = opt || {}; @@ -1022,6 +1026,34 @@ */ 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(){ console.log("user.alive() IS DEPRECATED!!!"); const gunRoot = this.back(-1) @@ -1036,6 +1068,7 @@ } } 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. //gun.get('alice').get('age').trust(bob); if (Gun.is(user)) { @@ -1128,25 +1161,24 @@ } */ module.exports = User - })(USE, './create'); + })(USE, './share'); ;USE(function(module){ - var SEA = USE('./sea') - var Gun = SEA.Gun; + var SEA = USE('./sea'), noop = function(){}, u; + 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. // 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); + at.on('put', check, at); // SEA now runs its firewall on HAM diffs, not all i/o. } 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. + // 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. // 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). @@ -1159,46 +1191,17 @@ // 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 } + //console.log('check', put, 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); eve.to.next(msg); - }); + })}) return } var no = function(why){ at.on('in', {'@': id, err: why}) }; @@ -1208,7 +1211,7 @@ // 'a~pub.key/b':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; + SEA.opt.pack = function(d,cb,k, n,s){ var tmp, f; // pack for verifying + if(SEA.opt.check(d)){ return cb(d) } + if(d && d['#'] && d['.'] && d['>']){ tmp = d[':']; f = 1 } + JSON.parseAsync(f? tmp : d, function(err, meta){ + var sig = ((u !== (meta||'')[':']) && (meta||'')['~']); // or just ~ check? + if(!sig){ cb(d); return } + cb({m: {'#':s||d['#'],'.':k||d['.'],':':(meta||'')[':'],'>':d['>']||Gun.state.is(n, k)}, s: sig}); + }); } var O = SEA.opt; SEA.opt.unpack = function(d, k, n){ var tmp; @@ -1459,7 +1316,7 @@ 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; + 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])){ return d[2]; } @@ -1468,9 +1325,8 @@ } } SEA.opt.shuffle_attack = 1546329600000; // Jan 1, 2019 - var noop = function(){}, u; var fl = Math.floor; // TODO: Still need to fix inconsistent state issue. // TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible. })(USE, './index'); -}()); +}()); \ No newline at end of file