From 577dea4c75ab49ef09df3bbdb63ea7c06e3036fe Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 19 Jun 2018 16:38:49 +0200 Subject: [PATCH] clientside implementation of block signing and encryption --- .gitignore | 1 + customize.dist/login.js | 2 +- rpc.js | 12 ++--- www/common/common-util.js | 15 +++++++ www/common/outer/login-block.js | 79 +++++++++++++++++++++++++++++++++ www/common/pinpad.js | 14 +++++- 6 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 www/common/outer/login-block.js diff --git a/.gitignore b/.gitignore index 741aedaf7..656e1f378 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ data npm-debug.log pins/ blob/ +block/ blobstage/ privileged.conf diff --git a/customize.dist/login.js b/customize.dist/login.js index 4d2855b16..17e3daab1 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -105,7 +105,7 @@ define([ return void cb('PASS_TOO_SHORT'); } - Cred.deriveFromPassphrase(uname, passwd, 128, function (bytes) { + Cred.deriveFromPassphrase(uname, passwd, 192, function (bytes) { // results... var res = { register: isRegister, diff --git a/rpc.js b/rpc.js index 6970c7736..f07af7c4c 100644 --- a/rpc.js +++ b/rpc.js @@ -1342,7 +1342,6 @@ var validateLoginBlock = function (Env, publicKey, signature, block, cb) { try { u8_block = Nacl.util.decodeBase64(block); } catch (e) { - // TODO print to console return void cb('E_INVALID_BLOCK'); } @@ -1432,8 +1431,6 @@ var writeLoginBlock = function (Env, msg, cb) { */ var removeLoginBlock = function (Env, msg, cb) { - console.log(msg); // XXX - var publicKey = msg[0]; var signature = msg[1]; var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant @@ -1854,9 +1851,12 @@ RPC.create = function ( Respond(e); }); case 'WRITE_LOGIN_BLOCK': - return void writeLoginBlock(Env, msg, function (e) { - // TODO handle response - e = e; + return void writeLoginBlock(Env, msg[1], function (e) { + if (e) { + WARN(e, 'WRITE_LOGIN_BLOCK'); + return void Respond(e); + } + Respond(e); }); case 'REMOVE_LOGIN_BLOCK': return void removeLoginBlock(Env, msg, function (e) { diff --git a/www/common/common-util.js b/www/common/common-util.js index a67ba6673..2a30133e9 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -83,6 +83,21 @@ define([], function () { }).join(''); }; + // given an array of Uint8Arrays, return a new Array with all their values + Util.uint8ArrayJoin = function (AA) { + var l = 0; + var i = 0; + for (; i < AA.length; i++) { l += AA[i].length; } + var C = new Uint8Array(l); + + i = 0; + for (var offset = 0; i < AA.length; i++) { + C.set(AA[i], offset); + offset += AA[i].length; + } + return C; + }; + Util.deduplicateString = function (array) { var a = array.slice(); for(var i=0; i + + // signature + + // block + + // [b64_public, b64_sig, b64_block [version, nonce, content]] + + Block.seed = function () { + return Nacl.hash(Nacl.util.decodeUTF8('pewpewpew')); + }; + + // should be deterministic from a seed... + Block.genkeys = function (seed) { + if (!seed || typeof(seed.length) !== 'number' || seed.length < 64) { + throw new Error('INVALID_SEED_LENGTH'); + } + + var signSeed = seed.subarray(0, Nacl.sign.seedLength); + var symmetric = seed.subarray(Nacl.sign.seedLength, + Nacl.sign.seedLength + Nacl.secretbox.keyLength); + + return { + sign: Nacl.sign.keyPair.fromSeed(signSeed), // 32 bytes + symmetric: symmetric, + }; + }; + + // (UTF8 content, keys object) => Uint8Array block + Block.encrypt = function (version, content, keys) { + var u8 = Nacl.util.decodeUTF8(content); + var nonce = Nacl.randomBytes(Nacl.secretbox.nonceLength); + return Block.join([ + [0], + nonce, + Nacl.secretbox(u8, nonce, keys.symmetric) + ]); + }; + + // (uint8Array block) => payload object + Block.decrypt = function (u8_content, keys) { + // version is currently ignored since there is only one + var nonce = u8_content.subarray(1, 1 + Nacl.secretbox.nonceLength); + var box = content.subarray(1 + Nacl.secretbox.nonceLength); + return Nacl.secretbox.open(box, nonce, keys.symmetric); + }; + + // (Uint8Array block) => signature + Block.sign = function (ciphertext, keys) { + return Nacl.sign.detached(Nacl.hash(ciphertext), keys.sign.secretKey); + }; + + Block.serialize = function (content, keys) { + // encrypt the content + var ciphertext = Block.encrypt(0, content, keys); + + // generate a detached signature + var sig = Block.sign(ciphertext, keys); + + // serialize {publickey, sig, ciphertext} + return { + publicKey: Nacl.util.encodeBase64(keys.sign.publicKey), + signature: Nacl.util.encodeBase64(sig), + ciphertext: Nacl.util.encodeBase64(ciphertext), + }; + }; + + return Block; +}); diff --git a/www/common/pinpad.js b/www/common/pinpad.js index 1e4dd6046..da1e51d28 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -222,7 +222,19 @@ define([ }; exp.writeLoginBlock = function (data, cb) { - cb(); + if (!data) { return void cb('NO_DATA'); } + if (!data.publicKey || !data.signature || !data.ciphertext) { + console.log(data); + return void cb("MISSING_PARAMETERS"); + } + + rpc.send('WRITE_LOGIN_BLOCK', [ + data.publicKey, + data.signature, + data.ciphertext + ], function (e) { + cb(e); + }); }; cb(e, exp);