This commit is contained in:
Mark Nadal 2019-01-06 22:01:40 -08:00
parent 2c110df395
commit 04d46e00ad
3 changed files with 90 additions and 28 deletions

70
sea.js
View File

@ -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);

View File

@ -17,7 +17,12 @@
<script src="./expect.js"></script>
<script></script>
<script src="../gun.js"></script>
<script src="../sea.js"></script>
<!-- script
src="https://cdn.jsdelivr.net/npm/gun@0.9.99999/sea.js">
</script -->
<script
src="../sea.js">
</script>
<script src="./common.js"></script>
<script src="./sea/sea.js"></script>
<script>
@ -26,6 +31,7 @@
console.log('async?', Gun.debug);
}
mocha.run(function(a,b,c){
//document.body.prepend("MARK! REMEMBER TO REMOVE RETURN!");return;
var yes = confirm("REFRESH BROWSER FOR ASYNC TESTS?");
if(yes){
if(location.search){

View File

@ -25,7 +25,7 @@ var SEA = Gun.SEA
if(!SEA){ return }
describe.only('SEA', function(){
describe('SEA', function(){
var user;
var gun;
describe('Utility', function(){
@ -153,25 +153,57 @@ describe.only('SEA', function(){
done();
})
it('legacy', async function(done){
var pw = 'test123';
// https://cdn.jsdelivr.net/npm/gun@0.9.99999/sea.js !
var old = JSON.parse(atob("eyJfIjp7IiMiOiJ+TkJhdDdKeUk0REw1ZDlPMEZNbWVFN0RacVZRZUVPblhKcldycDVUUGlyMC5PckV6WVIwc3h0NHRtV0tiajFQdHRaeW1HUmdyc1FVVDNHaTk1UE9vMUdBIiwiPiI6eyJwdWIiOjEsImFsaWFzIjoxLCJlcHViIjoxLCJhdXRoIjoxfX0sInB1YiI6Ik5CYXQ3SnlJNERMNWQ5TzBGTW1lRTdEWnFWUWVFT25YSnJXcnA1VFBpcjAuT3JFellSMHN4dDR0bVdLYmoxUHR0WnltR1JncnNRVVQzR2k5NVBPbzFHQSIsImFsaWFzIjoiU0VBe1wibVwiOlwiXFxcImJvYlxcXCJcIixcInNcIjpcIt4uXFx1MDAwNCpbcECT/sxe83eYe/M+bmBF+q5dQr7eYELndMJkXFx1MDAwYlxcbtFu6HNWUKh6XFxyfrWqwcRcXHUwMDE1e3BMv2poWlxcYktcXHUwMDEzZ5H/Z5VcIn0iLCJlcHViIjoiU0VBe1wibVwiOlwiXFxcIkdJUGY2dl8zeV9DZUpQMWtFZkt2OWpmZ3QwT2ZGeDRycHBKS01wSE9MLVEuTmM2dElDUlpwbGwxMG45V2NsRzhXNC1tdDFXZnI2cmh3c0JyN1pRTlduY1xcXCJcIixcInNcIjpcIlxcdTAwMTZcXHUwMDAwzVxcdTAwMGahrvVcXHUwMDBm9y77iP1V3IhkWOajKMxcXHUwMDEy/VxcdTAwMDHN+VxcbozxNWRcXHUwMDA1Zej5XFx1MDAwMpSOXFx1MDAwNny4IclB+lxcdTAwMWTgoXnR8S1OyuZcXHUwMDAx9PqwXFxiXFx1MDAwMFF3XCJ9IiwiYXV0aCI6IlNFQXtcIm1cIjpcIntcXFwiZWtcXFwiOlxcXCJTRUF7XFxcXFxcXCJjdFxcXFxcXFwiOlxcXFxcXFwiXFxcXFxcXFx1MDAwMGvAI6W0L03DwFxcXFxcXFxcdTAwMDZcXFxcXFxcXHUwMDA0ZibqQdE0XFxcXFxcXFx1MDAxY4VvtTZcXFxcXFxcXG7xXfBcXFxcXFxcXHUwMDAzo5xcXFxcXFxcXHUwMDE3XFxcXFxcXFx1MDAwMf9PXFxcXFxcXFx1MDAxMJhnXFxcXFxcXFx1MDAwNccti2pifouBhtu7qcw4/mPs1SHS4uyBTo1RTuReXFxcXFxcXFx1MDAxMK9W4clcXFxcXFxcXHUwMDBmYt1oSIRcXFxcXFxcXHUwMDE4PF5gxoRS2UYtV/1LwHn1SlxcXFxcXFxcXFxcXFxcXFyYuFU3cUVf09/AXFxcXFxcXFx1MDAwZlxcXFxcXFxcdTAwMDRQN8RlXFxcXFxcXFx1MDAwNlxcXFxcXFxcdTAwMGXM4G3fXFxcXFxcXFx1MDAxZt+eRoV9XFxcXFxcXCIsXFxcXFxcXCJpdlxcXFxcXFwiOlxcXFxcXFwiVU5Lv+Zko1xcXFxcXFxcdTAwMDOt1ET2JHhcXFxcXFxcXHUwMDE1/1xcXFxcXFwiLFxcXFxcXFwic1xcXFxcXFwiOlxcXFxcXFwiz0VOO9GwaJlcXFxcXFxcIn1cXFwiLFxcXCJzXFxcIjpcXFwiZ0F4TFJpa2dEakIzbXJDNGpucUFRak5NNEZXemF0a1Eyb2xDR2Z5TTc2amg3azNEUzAyRlp1MEV1eWg2RGFITlxcXCJ9XCIsXCJzXCI6XCKze+BcXHUwMDBilPlcXHUwMDA2z1srodVcXHUwMDA0P1xcXCJcXFwib2rndUadtqJcXHUwMDE2bFtf0PSvJNdcXHUwMDE2Y71nnlxcdTAwMWOZXFx1MDAwN1xcdTAwMTlcXHUwMDE36NZcXHUwMDA0Uk7DQK/y/oixrIr1XFx1MDAxZnVcXHUwMDE3oCBhXCJ9In0="));
var okey = {"pub":"NBat7JyI4DL5d9O0FMmeE7DZqVQeEOnXJrWrp5TPir0.OrEzYR0sxt4tmWKbj1PttZymGRgrsQUT3Gi95POo1GA","epub":"GIPf6v_3y_CeJP1kEfKv9jfgt0OfFx4rppJKMpHOL-Q.Nc6tICRZpll10n9WclG8W4-mt1Wfr6rhwsBr7ZQNWnc","priv":"leIA-BOFLECsOOdT_B8B0s1Ii0VHZZGlHz8q_dK-xLs","epriv":"1BTJpYdwSLesrtuB7pYQdsrFHsxKSJ-d9PXt2qp6NyQ"}
var auth = await SEA.verify(old.auth, old.pub);
var proof = await SEA.work(pw, auth.s, null, {encode: 'utf8'});
var dec = await SEA.decrypt(auth.ek, proof);
expect(dec.priv).to.be(okey.priv);
expect(dec.epriv).to.be(okey.epriv);
var gun = Gun(), tmp = Gun.node.soul(old);
var graph = {};
graph[tmp] = old;
var alias = await SEA.verify(old.alias, false);
expect(alias).to.be('bob');
alias = Gun.state.ify({}, tmp, 1, Gun.val.rel.ify(tmp), tmp = '~@'+alias);
graph[tmp] = alias;
gun.on('put', {$: gun, put: graph});
var use = gun.user();
use.auth('bob', 'test123', function(ack){
expect(ack.err).to.not.be.ok();
done();
});
console.log("+", gun._);
})
});
describe('User', function(){
it('is instantiable', function(done){
gun = Gun();
user = gun.user();
user = window.user = gun.user();
done();
})
it('register users', function(done){
user.create('bob', 'test123', function(ack){
/*var p = SEA.pair;
SEA.pair = async function(a,b,c,d){
var r = await p(a,b,c,d);
console.log("++++++++++++", r);
return r;
}*/
user.create('alice', 'test123', function(ack){
expect(ack.err).to.not.be.ok();
setTimeout(done, 30)
})
})
it('login users', function(done){
user.auth('bob', 'test123', function(ack){
user.auth('alice', 'test123', function(ack){
expect(ack.err).to.not.be.ok();
done()
})