diff --git a/sea.js b/sea.js index 2384fbbb..7c8fc7bd 100644 --- a/sea.js +++ b/sea.js @@ -384,7 +384,8 @@ var hash = await sha(data); var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign']) .then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here! - var r = 'SEA'+JSON.stringify({m: data, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')}); + var r = {m: data, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')} + if(!opt.raw){ r = 'SEA'+JSON.stringify(r) } if(cb){ try{ cb(r) }catch(e){console.log(e)} } return r; @@ -407,14 +408,6 @@ var parse = USE('./parse'); var u; - var knownKeys = {}; - var keyForPair = pair => { - if (knownKeys[pair]) return knownKeys[pair]; - var jwk = S.jwk(pair); - knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]); - return knownKeys[pair]; - }; - SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try { var json = parse(data); if(false === pair){ // don't verify! @@ -427,18 +420,17 @@ opt = opt || {}; // SEA.I // verify is free! Requires no user permission. var pub = pair.pub || pair; - var key = await keyForPair(pub); + var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']); var hash = await sha(json.m); - var buf; var sig; var check; try{ + var buf, sig, check, tmp; try{ buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT! sig = new Uint8Array(buf); check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)); if(!check){ throw "Signature did not match." } }catch(e){ - buf = shim.Buffer.from(json.s, 'utf8'); // AUTO BACKWARD OLD UTF8 DATA! - sig = new Uint8Array(buf); - check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)); - if(!check){ throw "Signature did not match." } + if(SEA.opt.fallback){ + return await SEA.opt.fall_verify(data, pair, cb, opt); + } } var r = check? parse(json.m) : u; @@ -453,6 +445,38 @@ }}); module.exports = SEA.verify; + // legacy & ossl leak mitigation: + + var knownKeys = {}; + var keyForPair = SEA.opt.slow_leak = pair => { + if (knownKeys[pair]) return knownKeys[pair]; + var jwk = S.jwk(pair); + knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]); + return knownKeys[pair]; + }; + + + 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 json = 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(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) + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + }catch(e){ + buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA! + sig = new Uint8Array(buf) + check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash)) + if(!check){ throw "Signature did not match." } + } + var r = check? parse(json.m) : u; + if(cb){ try{ cb(r) }catch(e){console.log(e)} } + return r; + } + SEA.opt.fallback = 2; + })(USE, './verify'); ;USE(function(module){ @@ -745,19 +769,19 @@ } // the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now! act.data = {pub: pair.pub}; - SEA.sign(alias, pair, act.d); + act.d(); //SEA.sign(alias, pair, act.d); } - act.d = function(sig){ - act.data.alias = alias || sig; - SEA.sign(act.pair.epub, act.pair, act.e); + act.d = function(){ + act.data.alias = alias; + act.e(); //SEA.sign(act.pair.epub, act.pair, act.e); } - act.e = function(epub){ - act.data.epub = act.pair.epub || epub; + act.e = function(){ + act.data.epub = act.pair.epub; SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f); // to keep the private key safe, we AES encrypt it with the proof of work! } act.f = function(auth){ act.data.auth = JSON.stringify({ek: auth, s: act.salt}); - SEA.sign({ek: auth, s: act.salt}, act.pair, act.g); + act.g(act.data.auth); //SEA.sign({ek: auth, s: act.salt}, act.pair, act.g); } act.g = function(auth){ var tmp; act.data.auth = act.data.auth || auth; @@ -1063,7 +1087,7 @@ // 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){ c++; // for each property on the node - // TODO: consider async/await use here... + // only process if SEA formatted? SEA.verify(val, false, function(data){ c--; // false just extracts the plain data. var tmp = data; data = SEA.opt.unpack(data, key, node); diff --git a/test/mocha.html b/test/mocha.html index 13aee439..ac1e3e17 100644 --- a/test/mocha.html +++ b/test/mocha.html @@ -17,7 +17,12 @@ - + +