diff --git a/customize.dist/login.js b/customize.dist/login.js index fa950afc2..9cb9765fb 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -392,7 +392,7 @@ define([ // send an RPC to store the block which you created. console.log("initializing rpc interface"); - Pinpad.create(RT.network, RT.proxy, waitFor(function (e, _rpc) { + Pinpad.create(RT.network, Block.keysToRPCFormat(res.opt.blockKeys), waitFor(function (e, _rpc) { if (e) { waitFor.abort(); console.error(e); // INVALID_KEYS diff --git a/lib/commands/block.js b/lib/commands/block.js index 288089a16..8180cb68e 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -86,7 +86,7 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey); }; -var validateAncestorProof = function (Env, proof, _cb) { +Block.validateAncestorProof = function (Env, proof, _cb) { var cb = Util.once(Util.mkAsync(_cb)); /* prove that you own an existing block by signing for its publicKey */ try { @@ -97,7 +97,6 @@ var validateAncestorProof = function (Env, proof, _cb) { var u8_sig = Nacl.util.decodeBase64(sig); var valid = false; nThen(function (w) { - // XXX restricted-registration do this in a worker valid = Nacl.sign.detached.verify(u8_pub, u8_sig, u8_pub); if (!valid) { w.abort(); @@ -130,14 +129,23 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS var validatedBlock, parsed, path; nThen(function (w) { + if (Util.escapeKeyCharacters(publicKey) !== safeKey) { + w.abort(); + return void cb("INCORRECT_KEY"); + } + }).nThen(function (w) { if (!Env.restrictRegistration) { return; } if (!registrationProof) { // we allow users with existing blocks to create new ones // call back with error if registration is restricted and no proof of an existing block was provided w.abort(); + Env.Log.info("BLOCK_REJECTED_REGISTRATION", { + safeKey: safeKey, + publicKey: publicKey, + }); return cb("E_RESTRICTED"); } - validateAncestorProof(Env, registrationProof, w(function (err, provenKey) { + Env.validateAncestorProof(registrationProof, w(function (err, provenKey) { if (err || !provenKey) { // double check that a key was validated w.abort(); Env.Log.warn('BLOCK_REJECTED_INVALID_ANCESTOR', { @@ -191,6 +199,7 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS blockId: publicKey, isChange: Boolean(registrationProof), previousKey: previousKey, + path: path, }); cb(); }); @@ -212,26 +221,33 @@ Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS var signature = msg[1]; var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant - validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { - if (e) { return void cb(e); } - // derive the filepath - var path = createLoginBlockPath(Env, publicKey); - - // make sure the path is valid - if (typeof(path) !== 'string') { - return void cb('E_INVALID_BLOCK_PATH'); + nThen(function (w) { + if (Util.escapeKeyCharacters(publicKey) !== safeKey) { + w.abort(); + return void cb("INCORRECT_KEY"); } + }).nThen(function () { + validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { + if (e) { return void cb(e); } + // derive the filepath + var path = createLoginBlockPath(Env, publicKey); - // FIXME COLDSTORAGE - Fs.unlink(path, function (err) { - Env.Log.info('DELETION_BLOCK_BY_OWNER_RPC', { - publicKey: publicKey, - path: path, - status: err? String(err): 'SUCCESS', + // make sure the path is valid + if (typeof(path) !== 'string') { + return void cb('E_INVALID_BLOCK_PATH'); + } + + // FIXME COLDSTORAGE + Fs.unlink(path, function (err) { + Env.Log.info('DELETION_BLOCK_BY_OWNER_RPC', { + publicKey: publicKey, + path: path, + status: err? String(err): 'SUCCESS', + }); + + if (err) { return void cb(err); } + cb(); }); - - if (err) { return void cb(err); } - cb(); }); }); }; diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index fb7a5ebc8..daa041b5f 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -158,6 +158,7 @@ module.exports.create = function (Env, cb) { pinPath: Env.paths.pin, filePath: Env.paths.data, archivePath: Env.paths.archive, + blockPath: Env.paths.block, inactiveTime: Env.inactiveTime, archiveRetentionTime: Env.archiveRetentionTime, diff --git a/lib/storage/blob.js b/lib/storage/blob.js index 044eeaeaa..e5c7a2fce 100644 --- a/lib/storage/blob.js +++ b/lib/storage/blob.js @@ -295,7 +295,7 @@ var owned_upload_complete = function (Env, safeKey, id, cb) { // removeBlob var remove = function (Env, blobId, cb) { var blobPath = makeBlobPath(Env, blobId); - Fs.unlink(blobPath, cb); // TODO COLDSTORAGE + Fs.unlink(blobPath, cb); }; // removeProof diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 65f13d23b..8585cc3f6 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -4,6 +4,7 @@ const HK = require("../hk-util"); const Store = require("../storage/file"); const BlobStore = require("../storage/blob"); +const Block = require("../commands/block"); const Util = require("../common-util"); const nThen = require("nthen"); const Meta = require("../metadata"); @@ -47,6 +48,7 @@ const init = function (config, _cb) { Env.paths = { pin: config.pinPath, + block: config.blockPath, }; Env.inactiveTime = config.inactiveTime; @@ -688,6 +690,10 @@ COMMANDS.HASH_CHANNEL_LIST = function (data, cb) { cb(void 0, hash); }; +COMMANDS.VALIDATE_ANCESTOR_PROOF = function (data, cb) { + Block.validateAncestorProof(Env, data && data.proof, cb); +}; + process.on('message', function (data) { if (!data || !data.txid || !data.pid) { return void process.send({ diff --git a/lib/workers/index.js b/lib/workers/index.js index 25c18d947..85c66eeb5 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -444,6 +444,13 @@ Workers.initialize = function (Env, config, _cb) { }, cb); }; + Env.validateAncestorProof = function (proof, cb) { + sendCommand({ + command: 'VALIDATE_ANCESTOR_PROOF', + proof: proof, + }, cb); + }; + cb(void 0); }); }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 0da28a2bc..27eb48787 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1735,6 +1735,7 @@ define([ var removeData = obj.Block.remove(blockKeys); postMessage("DELETE_ACCOUNT", { + keys: Block.keysToRPCFormat(blockKeys), removeData: removeData }, function (obj) { if (obj.state) { @@ -1856,11 +1857,15 @@ define([ var content = Block.serialize(JSON.stringify(temp), blockKeys); console.error("OLD AND NEW BLOCK KEYS", oldBlockKeys, blockKeys); - // XXX ignored unless restricted registration is active? content.registrationProof = Block.proveAncestor(oldBlockKeys); console.log("writing new login block"); - common.writeLoginBlock(content, waitFor(function (obj) { + + var data = { + keys: Block.keysToRPCFormat(blockKeys), + content: content, + }; + common.writeLoginBlock(data, waitFor(function (obj) { if (obj && obj.error) { waitFor.abort(); return void cb(obj); @@ -1878,8 +1883,11 @@ define([ // Remove block hash if (blockHash) { console.log('removing old login block'); - var removeData = Block.remove(oldBlockKeys); - common.removeLoginBlock(removeData, waitFor(function (obj) { + var data = { + keys: Block.keysToRPCFormat(oldBlockKeys), // { edPrivate, edPublic } + content: Block.remove(oldBlockKeys), + }; + common.removeLoginBlock(data, waitFor(function (obj) { if (obj && obj.error) { return void console.error(obj.error); } })); } diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 3eb983fdb..21bc77798 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -451,19 +451,33 @@ define([ }; Store.writeLoginBlock = function (clientId, data, cb) { - store.rpc.writeLoginBlock(data, function (e, res) { - cb({ - error: e, - data: res + Pinpad.create(store.network, data && data.keys, function (err, rpc) { + if (err) { + return void cb({ + error: err, + }); + } + rpc.writeLoginBlock(data && data.content, function (e, res) { + cb({ + error: e, + data: res + }); }); }); }; Store.removeLoginBlock = function (clientId, data, cb) { - store.rpc.removeLoginBlock(data, function (e, res) { - cb({ - error: e, - data: res + Pinpad.create(store.network, data && data.keys, function (err, rpc) { + if (err) { + return void cb({ + error: err, + }); + } + rpc.removeLoginBlock(data && data.content, function (e, res) { + cb({ + error: e, + data: res + }); }); }); }; @@ -815,6 +829,7 @@ define([ Store.deleteAccount = function (clientId, data, cb) { var edPublic = store.proxy.edPublic; var removeData = data && data.removeData; + var rpcKeys = data && data.keys; Store.anonRpcMsg(clientId, { msg: 'GET_METADATA', data: store.driveChannel @@ -845,8 +860,15 @@ define([ }, waitFor()); }).nThen(function (waitFor) { if (!removeData) { return; } - // Delete the block. Don't abort if it fails, it doesn't leak any data. - store.rpc.removeLoginBlock(removeData, waitFor()); + var done = waitFor(); + Pinpad.create(store.network, rpcKeys, function (err, rpc) { + if (err) { + console.error(err); + return void done(); + } + // Delete the block. Don't abort if it fails, it doesn't leak any data. + rpc.removeLoginBlock(removeData, done); + }); }).nThen(function () { // Log out current worker postMessage(clientId, "DELETE_ACCOUNT", token, function () {}); diff --git a/www/common/outer/login-block.js b/www/common/outer/login-block.js index 9950e18af..0d75fdfca 100644 --- a/www/common/outer/login-block.js +++ b/www/common/outer/login-block.js @@ -40,6 +40,19 @@ define([ }; }; + Block.keysToRPCFormat = function (keys) { + try { + var sign = keys.sign; + return { + edPrivate: Nacl.util.encodeBase64(sign.secretKey), + edPublic: Nacl.util.encodeBase64(sign.publicKey), + }; + } catch (err) { + console.error(err); + return; + } + }; + // (UTF8 content, keys object) => Uint8Array block Block.encrypt = function (version, content, keys) { var u8 = Nacl.util.decodeUTF8(content);