check for the presence of a blockHash in localStorage when logging in

This commit is contained in:
ansuz 2018-06-20 14:27:44 +02:00
parent 3ba0ad3cf1
commit d03339f20b
7 changed files with 157 additions and 14 deletions

3
rpc.js
View file

@ -1380,8 +1380,7 @@ var createLoginBlockPath = function (Env, publicKey) {
}; };
var writeLoginBlock = function (Env, msg, cb) { var writeLoginBlock = function (Env, msg, cb) {
console.log(msg); // XXX //console.log(msg);
var publicKey = msg[0]; var publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = msg[2]; var block = msg[2];

View file

@ -10,9 +10,12 @@ define([
'/common/wire.js', '/common/wire.js',
'/common/flat-dom.js', '/common/flat-dom.js',
'/common/media-tag.js', '/common/media-tag.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, Hyperjson, Sortify, Drive, Test, Hash, Util, Thumb, Wire, Flat, MediaTag) { ], function ($, Hyperjson, Sortify, Drive, Test, Hash, Util, Thumb, Wire, Flat, MediaTag) {
window.Hyperjson = Hyperjson; window.Hyperjson = Hyperjson;
window.Sortify = Sortify; window.Sortify = Sortify;
var Nacl = window.nacl;
var assertions = 0; var assertions = 0;
var failed = false; var failed = false;
@ -296,6 +299,18 @@ define([
!secret.hashData.present); !secret.hashData.present);
}, "test support for ugly tracking query paramaters in url"); }, "test support for ugly tracking query paramaters in url");
assert(function (cb) {
var href = 'https://cryptpad.fr/block/pe/pewpewpewpewpew';
var key = Nacl.randomBytes(32);
var hash = Hash.createBlockHash(href, key);
var parsed = Hash.parseBlockHash(hash);
cb(parsed && href === parsed.href &&
parsed.keys.symmetric.length === key.length);
}, 'parse a block hash');
assert(function (cb) { assert(function (cb) {
try { try {
MediaTag(void 0).on('progress').on('decryption'); MediaTag(void 0).on('progress').on('decryption');

View file

@ -474,6 +474,43 @@ Version 1
'/' + curvePublic.replace(/\//g, '-') + '/'; '/' + curvePublic.replace(/\//g, '-') + '/';
}; };
// XXX consider putting Block functions in /common/outer/login-block.js
Hash.createBlockHash = function (href, key) {
if (typeof(href) !== 'string') { return; }
if (!key instanceof Uint8Array) { return; }
// TODO verify inputs
try { return href + '#' + Nacl.util.encodeBase64(key); }
catch (e) { return; }
};
var decodeSafeB64 = function (b64) {
try {
return Nacl.util.decodeBase64(b64.replace(/\-/g, '/'));
} catch (e) {
console.error(e);
return;
}
};
Hash.parseBlockHash = function (hash) {
if (typeof(hash) !== 'string') { return; }
var parts = hash.split('#');
if (parts.length !== 2) { return; }
try {
return {
href: parts[0],
keys: {
symmetric: decodeSafeB64(parts[1]),
}
};
} catch (e) {
console.error(e);
return;
}
};
// Create untitled documents when no name is given // Create untitled documents when no name is given
var getLocaleDate = function () { var getLocaleDate = function () {
if (window.Intl && window.Intl.DateTimeFormat) { if (window.Intl && window.Intl.DateTimeFormat) {

View file

@ -137,17 +137,15 @@ define([], function () {
else if (bytes >= oneMegabyte) { return 'MB'; } else if (bytes >= oneMegabyte) { return 'MB'; }
}; };
// given a path, asynchronously return an arraybuffer
Util.fetch = function (src, cb) { Util.fetch = function (src, cb) {
var done = false; var done = false;
var CB = function (err, res) { var CB = Util.once(cb);
if (done) { return; }
done = true;
cb(err, res);
};
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open("GET", src, true); xhr.open("GET", src, true);
xhr.responseType = "arraybuffer"; xhr.responseType = "arraybuffer";
xhr.onerror = function (err) { CB(err); };
xhr.onload = function () { xhr.onload = function () {
if (/^4/.test(''+this.status)) { if (/^4/.test(''+this.status)) {
return CB('XHR_ERROR'); return CB('XHR_ERROR');

View file

@ -8,11 +8,12 @@ define([
'/common/common-feedback.js', '/common/common-feedback.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/common/outer/worker-channel.js', '/common/outer/worker-channel.js',
'/common/outer/login-block.js',
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
], function (Config, Messages, Util, Hash, ], function (Config, Messages, Util, Hash,
Messaging, Constants, Feedback, LocalStore, Channel, Messaging, Constants, Feedback, LocalStore, Channel, Block,
AppConfig, Nthen) { AppConfig, Nthen) {
/* This file exposes functionality which is specific to Cryptpad, but not to /* This file exposes functionality which is specific to Cryptpad, but not to
@ -883,7 +884,46 @@ define([
if (AppConfig.beforeLogin) { if (AppConfig.beforeLogin) {
AppConfig.beforeLogin(LocalStore.isLoggedIn(), waitFor()); AppConfig.beforeLogin(LocalStore.isLoggedIn(), waitFor());
} }
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
var blockHash = LocalStore.getBlockHash();
if (blockHash) {
console.log(blockHash);
var parsed = Hash.parseBlockHash(blockHash);
if (typeof(parsed) !== 'object') {
console.error("Failed to parse blockHash");
console.log(parsed);
return;
} else {
console.log(parsed);
}
Util.fetch(parsed.href, waitFor(function (err, arraybuffer) {
if (err) { return void console.log(err); }
// use the results to load your user hash and
// put your userhash into localStorage
try {
var block_info = Block.decrypt(arraybuffer, parsed.keys);
if (block_info[Constants.userHashKey]) { LocalStore.setUserHash(block_info[Constants.userHashKey]); }
} catch (e) {
console.error(e);
return void console.error("failed to decrypt or decode block content");
}
}));
} else {
// XXX debugging
console.error("NO BLOCK HASH");
}
}).nThen(function (waitFor) {
// XXX debugging
if (LocalStore.getUserHash()) {
console.log('User_hash detected');
} else {
console.log("User_hash not detected");
}
var cfg = { var cfg = {
init: true, init: true,
//query: onMessage, // TODO temporary, will be replaced by a webworker channel //query: onMessage, // TODO temporary, will be replaced by a webworker channel

View file

@ -1,7 +1,8 @@
define([ define([
'/common/common-util.js', '/common/common-util.js',
'/api/config',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
], function (Util) { ], function (Util, ApiConfig) {
var Nacl = window.nacl; var Nacl = window.nacl;
var Block = {}; var Block = {};
@ -30,9 +31,11 @@ define([
var symmetric = seed.subarray(Nacl.sign.seedLength, var symmetric = seed.subarray(Nacl.sign.seedLength,
Nacl.sign.seedLength + Nacl.secretbox.keyLength); Nacl.sign.seedLength + Nacl.secretbox.keyLength);
console.log("symmetric key: ", Nacl.util.encodeBase64(symmetric));
return { return {
sign: Nacl.sign.keyPair.fromSeed(signSeed), // 32 bytes sign: Nacl.sign.keyPair.fromSeed(signSeed), // 32 bytes
symmetric: symmetric, symmetric: symmetric, // 32 bytes ...
}; };
}; };
@ -51,8 +54,15 @@ define([
Block.decrypt = function (u8_content, keys) { Block.decrypt = function (u8_content, keys) {
// version is currently ignored since there is only one // version is currently ignored since there is only one
var nonce = u8_content.subarray(1, 1 + Nacl.secretbox.nonceLength); var nonce = u8_content.subarray(1, 1 + Nacl.secretbox.nonceLength);
var box = content.subarray(1 + Nacl.secretbox.nonceLength); var box = u8_content.subarray(1 + Nacl.secretbox.nonceLength);
return Nacl.secretbox.open(box, nonce, keys.symmetric);
var plaintext = Nacl.secretbox.open(box, nonce, keys.symmetric);
try {
return JSON.parse(Nacl.util.encodeUTF8(plaintext));
} catch (e) {
console.error(e);
return;
}
}; };
// (Uint8Array block) => signature // (Uint8Array block) => signature
@ -86,5 +96,18 @@ define([
}; };
}; };
// FIXME don't spread the functions below across this file and common-hash
// find a permanent home for these hacks
var urlSafeB64 = function (u8) {
return Nacl.util.encodeBase64(u8).replace(/\//g, '-');
};
Block.getBlockHash = function (keys) {
var publicKey = urlSafeB64(keys.sign.publicKey);
var relative = 'block/' + publicKey.slice(0, 2) + '/' + publicKey; // XXX FIXME use configurable path from /api/config
var symmetric = urlSafeB64(keys.symmetric);
return ApiConfig.httpUnsafeOrigin + relative + '#' + symmetric;
};
return Block; return Block;
}); });

View file

@ -11,6 +11,7 @@ define([
'/common/hyperscript.js', '/common/hyperscript.js',
'/customize/application_config.js', '/customize/application_config.js',
'/api/config', '/api/config',
'/common/outer/login-block.js', // XXX HACK
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
@ -28,7 +29,8 @@ define([
Messages, Messages,
h, h,
AppConfig, AppConfig,
ApiConfig ApiConfig,
Block // XXX HACK
) )
{ {
var saveAs = window.saveAs; var saveAs = window.saveAs;
@ -389,7 +391,36 @@ define([
}); });
}; };
updateBlock = updateBlock; // jshint.. var removeBlock = function (data, cb) {
sframeChan.query('Q_REMOVE_LOGIN_BLOCK', data, function (err, obj) {
if (err || obj.error) { return void cb ({error: err || obj.error}); }
cb (obj);
});
};
// XXX
if (false) { // STUBBED, just for development purposes
console.error("TRYING TO WRITE A BLOCK");
var keys = Block.genkeys(Block.seed());
var data = Block.serialize(JSON.stringify({
a: 5,
b: 6,
User_hash: "XXX", /// TODO encode newly derived User_hash here
}), keys);
updateBlock(data, function (err, thing) {
console.log(err, thing);
console.log(Block.getBlockHash(keys));
return;
removeBlock(Block.remove(keys), function (err, obj) {
console.log(err, obj);
});
});
}
return $div; return $div;
}; };