latest rpc code
parent
b50b76bf81
commit
a0c17d7c9b
123
rpc.js
123
rpc.js
|
@ -1,6 +1,4 @@
|
|||
/* Use Nacl for checking signatures of messages
|
||||
|
||||
*/
|
||||
/* Use Nacl for checking signatures of messages */
|
||||
var Nacl = require("tweetnacl");
|
||||
|
||||
var RPC = module.exports;
|
||||
|
@ -15,43 +13,124 @@ var isValidChannel = function (chan) {
|
|||
return /^[a-fA-F0-9]/.test(chan);
|
||||
};
|
||||
|
||||
var checkSignature = function (signedMsg, publicKey) {
|
||||
if (!(signedMsg && publicKey)) { return null; }
|
||||
var makeCookie = function (seq) {
|
||||
return [
|
||||
Math.floor(new Date() / (1000*60*60*24)),
|
||||
process.pid, // jshint ignore:line
|
||||
seq
|
||||
].join('|');
|
||||
};
|
||||
|
||||
var parseCookie = function (cookie) {
|
||||
if (!(cookie && cookie.split)) { return null; }
|
||||
|
||||
var parts = cookie.split('|');
|
||||
if (parts.length !== 3) { return null; }
|
||||
|
||||
var c = {};
|
||||
c.time = new Date(parts[0]);
|
||||
c.pid = parts[1];
|
||||
c.seq = parts[2];
|
||||
return c;
|
||||
};
|
||||
|
||||
var isValidCookie = function (ctx, cookie) {
|
||||
var now = +new Date();
|
||||
if (now - cookie.time > 300000) { // 5 minutes
|
||||
return false;
|
||||
}
|
||||
|
||||
// different process. try harder
|
||||
if (process.pid !== cookie.pid) { // jshint ignore:line
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (cookie.seq !==
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var checkSignature = function (signedMsg, signature, publicKey) {
|
||||
if (!(signedMsg && publicKey)) { return false; }
|
||||
|
||||
var signedBuffer;
|
||||
var pubBuffer;
|
||||
var signatureBuffer;
|
||||
|
||||
try {
|
||||
signedBuffer = Nacl.util.decodeBase64(signedMsg);
|
||||
pubBuffer = Nacl.util.decodeBase64(publicKey);
|
||||
signedBuffer = Nacl.util.decodeUTF8(signedMsg);
|
||||
} catch (e) {
|
||||
console.log('invalid signedBuffer');
|
||||
console.log(signedMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
var opened = Nacl.sign.open(signedBuffer, pubBuffer);
|
||||
|
||||
if (opened) {
|
||||
var decoded = Nacl.util.encodeUTF8(opened);
|
||||
try {
|
||||
return JSON.parse(decoded);
|
||||
} catch (e) { } // fall through to return
|
||||
try {
|
||||
pubBuffer = Nacl.util.decodeBase64(publicKey);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
|
||||
try {
|
||||
signatureBuffer = Nacl.util.decodeBase64(signature);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pubBuffer.length !== 32) {
|
||||
console.log('public key length: ' + pubBuffer.length);
|
||||
console.log(publicKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (signatureBuffer.length !== 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Nacl.sign.detached.verify(signedBuffer, signatureBuffer, pubBuffer);
|
||||
};
|
||||
|
||||
RPC.create = function (config, cb) {
|
||||
// load pin-store...
|
||||
|
||||
console.log('loading rpc module...');
|
||||
var rpc = function (ctx, args, respond) {
|
||||
if (args.length < 2) {
|
||||
|
||||
var Cookies = {};
|
||||
|
||||
|
||||
|
||||
var rpc = function (ctx, data, respond) {
|
||||
if (!data.length) {
|
||||
return void respond("INSUFFICIENT_ARGS");
|
||||
} else if (data.length !== 1) {
|
||||
console.log(data.length);
|
||||
}
|
||||
|
||||
var signed = args[0];
|
||||
var publicKey = args[1];
|
||||
var msg = data[0].slice(0);
|
||||
var signature = msg.shift();
|
||||
var publicKey = msg.shift();
|
||||
var cookie = parseCookie(msg.shift());
|
||||
|
||||
var msg = checkSignature(signed, publicKey);
|
||||
if (!msg) {
|
||||
if (!cookie) {
|
||||
// no cookie is fine if the RPC is to get a cookie
|
||||
if (msg[0] !== 'COOKIE') {
|
||||
return void respond('NO_COOKIE');
|
||||
}
|
||||
} else if (!isValidCookie(cookie)) { // is it a valid cookie?
|
||||
return void respond('INVALID_COOKIE');
|
||||
}
|
||||
|
||||
var serialized = JSON.stringify(msg);
|
||||
|
||||
if (!(serialized && publicKey)) {
|
||||
return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
|
||||
}
|
||||
|
||||
if (checkSignature(serialized, signature, publicKey) !== true) {
|
||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||
}
|
||||
|
||||
if (!msg.length) {
|
||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||
}
|
||||
|
||||
|
@ -60,6 +139,8 @@ RPC.create = function (config, cb) {
|
|||
}
|
||||
|
||||
switch (msg[0]) {
|
||||
case 'COOKIE':
|
||||
return void respond(void 0, makeCookie());
|
||||
case 'ECHO':
|
||||
return void respond(void 0, msg);
|
||||
case 'RESET':
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
define([
|
||||
'/common/encode.js',
|
||||
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (Encode) {
|
||||
], function () {
|
||||
var MAX_LAG_BEFORE_TIMEOUT = 30000;
|
||||
var Nacl = window.nacl;
|
||||
|
||||
|
@ -11,34 +9,27 @@ define([
|
|||
.toString(32).replace(/\./g, '');
|
||||
};
|
||||
|
||||
var signMsg = function (type, msg, signKey) {
|
||||
var toSign = JSON.stringify([type, msg]);
|
||||
var buffer = Nacl.util.decodeUTF8(toSign);
|
||||
return Nacl.util.encodeBase64(Nacl.sign(buffer, signKey));
|
||||
var signMsg = function (data, signKey) {
|
||||
var buffer = Nacl.util.decodeUTF8(JSON.stringify(data));
|
||||
return Nacl.util.encodeBase64(Nacl.sign.detached(buffer, signKey));
|
||||
};
|
||||
|
||||
/*
|
||||
/*
|
||||
types of messages:
|
||||
pin -> hash
|
||||
unpin -> hash
|
||||
getHash -> hash
|
||||
getTotalSize -> bytes
|
||||
getFileSize -> bytes
|
||||
|
||||
*/
|
||||
|
||||
/* RPC communicates only with the history keeper
|
||||
messages have the format:
|
||||
[TYPE, txid, msg]
|
||||
*/
|
||||
var sendMsg = function (ctx, type, signed, id, cb) {
|
||||
|
||||
var sendMsg = function (ctx, data, cb) {
|
||||
var network = ctx.network;
|
||||
var hkn = network.historyKeeper;
|
||||
var txid = uid();
|
||||
|
||||
ctx.pending[txid] = cb;
|
||||
|
||||
return network.sendto(hkn, JSON.stringify([txid, signed, id]));
|
||||
return network.sendto(hkn, JSON.stringify([txid, data]));
|
||||
};
|
||||
|
||||
var parse = function (msg) {
|
||||
|
@ -49,20 +40,20 @@ types of messages:
|
|||
}
|
||||
};
|
||||
|
||||
/* Returning messages have the format:
|
||||
[txid, {}]
|
||||
*/
|
||||
var onMsg = function (ctx, msg) {
|
||||
var parsed = parse(msg);
|
||||
|
||||
if (!parsed) {
|
||||
// TODO handle error
|
||||
console.log(msg);
|
||||
return;
|
||||
return void console.error(new Error('could not parse message: %s', msg));
|
||||
}
|
||||
|
||||
var txid = parsed[0];
|
||||
var pending = ctx.pending[txid];
|
||||
|
||||
if (!(parsed && parsed.slice)) {
|
||||
return void console.error('MALFORMED_RPC_RESPONSE');
|
||||
}
|
||||
|
||||
var response = parsed.slice(1);
|
||||
|
||||
if (typeof(pending) === 'function') {
|
||||
|
@ -76,39 +67,57 @@ types of messages:
|
|||
};
|
||||
|
||||
var create = function (network, edPrivateKey, edPublicKey) {
|
||||
var signKey = Nacl.util.decodeBase64(edPrivateKey);
|
||||
var signKey;
|
||||
|
||||
try {
|
||||
signKey = Nacl.util.decodeBase64(edPrivateKey);
|
||||
if (signKey.length !== 64) {
|
||||
throw new Error('private key did not match expected length of 64');
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error("private signing key is not valid");
|
||||
}
|
||||
} catch (err) { throw err; }
|
||||
|
||||
// TODO validate public key as well
|
||||
var pubBuffer;
|
||||
try {
|
||||
pubBuffer = Nacl.util.decodeBase64(edPublicKey);
|
||||
if (pubBuffer.length !== 32) {
|
||||
throw new Error('expected public key to be 32 uint');
|
||||
}
|
||||
} catch (err) { throw err; }
|
||||
|
||||
var ctx = {
|
||||
//privateKey: Encode.hexToUint8Array(edPrivateKey),
|
||||
seq: new Date().getTime(),
|
||||
network: network,
|
||||
timeouts: {}, // timeouts
|
||||
pending: {}, // callbacks
|
||||
cookie: null,
|
||||
};
|
||||
|
||||
var pin = function (channel, cb) { };
|
||||
|
||||
var send = function (type, msg, cb) {
|
||||
// construct a signed message...
|
||||
var signed = signMsg(type, msg, signKey);
|
||||
var data = [type, msg];
|
||||
var sig = signMsg(data, signKey);
|
||||
|
||||
return sendMsg(ctx, type, signed, edPublicKey, cb);
|
||||
data.unshift(ctx.cookie); //
|
||||
data.unshift(edPublicKey);
|
||||
data.unshift(sig);
|
||||
|
||||
// [sig, edPublicKey, cookie, type, msg]
|
||||
return sendMsg(ctx, data, cb);
|
||||
};
|
||||
|
||||
var getCookie = function (cb) {
|
||||
send('COOKIE', "", function (e, msg) {
|
||||
console.log('cookie message', e, msg);
|
||||
cb(e, msg);
|
||||
});
|
||||
};
|
||||
|
||||
network.on('message', function (msg, sender) {
|
||||
onMsg(ctx, msg);
|
||||
});
|
||||
return {
|
||||
send: send,
|
||||
ready: getCookie,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -24,65 +24,72 @@ define([
|
|||
b: 7,
|
||||
};
|
||||
|
||||
// console.log(payload);
|
||||
rpc.send('ECHO', payload, function (e, msg) {
|
||||
if (e) { return void console.error(e); }
|
||||
console.log(msg);
|
||||
});
|
||||
rpc.ready(function () {
|
||||
|
||||
// test a non-existent RPC call
|
||||
rpc.send('PEWPEW', ['pew'], function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'UNSUPPORTED_RPC_CALL') { return; }
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(msg);
|
||||
});
|
||||
// console.log(payload);
|
||||
rpc.send('ECHO', payload, function (e, msg) {
|
||||
if (e) { return void console.error(e); }
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
var list = Cryptpad.getUserChannelList();
|
||||
if (list.length) {
|
||||
rpc.send('GET_FILE_SIZE', list[0], function (e, msg) {
|
||||
// test a non-existent RPC call
|
||||
rpc.send('PEWPEW', ['pew'], function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'UNSUPPORTED_RPC_CALL') { return; }
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
|
||||
rpc.send('GET_FILE_SIZE', 'pewpew', function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'INVALID_CHAN') { return; }
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
rpc.send('GET_FILE_SIZE', '26f014b2ab959418605ea37a6785f317', function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'ENOENT') { return; }
|
||||
return void console.error(e);
|
||||
}
|
||||
console.error("EXPECTED ENOENT");
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
(function () {
|
||||
var bytes = 0;
|
||||
list.forEach(function (chan) {
|
||||
rpc.send('GET_FILE_SIZE', chan, function (e, msg) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (msg && msg[0] && typeof(msg[0]) === 'number') {
|
||||
bytes += msg[0];
|
||||
console.log(bytes);
|
||||
} else {
|
||||
console.log(msg);
|
||||
|
||||
|
||||
var list = Cryptpad.getUserChannelList();
|
||||
if (list.length) {
|
||||
rpc.send('GET_FILE_SIZE', list[0], function (e, msg) {
|
||||
if (e) {
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(msg);
|
||||
});
|
||||
});
|
||||
}());
|
||||
}
|
||||
|
||||
rpc.send('GET_FILE_SIZE', 'pewpew', function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'INVALID_CHAN') { return; }
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
rpc.send('GET_FILE_SIZE', '26f014b2ab959418605ea37a6785f317', function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'ENOENT') { return; }
|
||||
return void console.error(e);
|
||||
}
|
||||
console.error("EXPECTED ENOENT");
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
if (false) {
|
||||
(function () {
|
||||
var bytes = 0;
|
||||
list.forEach(function (chan) {
|
||||
rpc.send('GET_FILE_SIZE', chan, function (e, msg) {
|
||||
if (e) {
|
||||
if (e === 'ENOENT') {
|
||||
return void console.log(e, chan);
|
||||
}
|
||||
return void console.error(e);
|
||||
}
|
||||
if (msg && msg[0] && typeof(msg[0]) === 'number') {
|
||||
bytes += msg[0];
|
||||
console.log(bytes);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
}());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue