Encrypted href

pull/1/head
yflory 5 years ago
parent cf0d0e8e4b
commit 5ab3f39fb8

@ -52,7 +52,8 @@ define([
var cancel = function () {
cancelled = true;
};
var parsed = Hash.parsePadUrl(fData.href || fData.roHref);
var href = (fData.href && fData.href.indexOf('#') !== -1) ? fData.href : fData.roHref;
var parsed = Hash.parsePadUrl(href);
var hash = parsed.hash;
var name = fData.filename || fData.title;
var secret = Hash.getSecrets('file', hash, fData.password);
@ -88,7 +89,8 @@ define([
cancelled = true;
};
var parsed = Hash.parsePadUrl(pData.href || pData.roHref);
var href = (pData.href && pData.href.indexOf('#') !== -1) ? pData.href : pData.roHref;
var parsed = Hash.parsePadUrl(href);
var name = pData.filename || pData.title;
var opts = {
password: pData.password
@ -137,7 +139,8 @@ define([
});
}
var parsed = Hash.parsePadUrl(fData.href || fData.roHref);
var href = (fData.href && fData.href.indexOf('#') !== -1) ? fData.href : fData.roHref;
var parsed = Hash.parsePadUrl(href);
if (['pad', 'file'].indexOf(parsed.hashData.type) === -1) { return; }
// waitFor is used to make sure all the pads and files are process before downloading the zip.

@ -38,8 +38,7 @@ define([
var data = oldFo.getFileData(id);
var channel = data.channel;
// XXX encrypted href: we need to be able to change the value here
var datas = manager.findChannel(channel, true);
var datas = manager.findChannel(channel);
// Do not migrate a pad if we already have it, it would create a duplicate
// in the drive
if (datas.length !== 0) {
@ -50,7 +49,9 @@ define([
// We want to merge an edit pad: check if we have the same channel
// but read-only and upgrade it in that case
datas.forEach(function (pad) {
if (pad.data && !pad.data.href) { pad.data.href = data.href; }
if (pad.data && !pad.data.href) {
pad.userObject.setHref(channel, null, data.href);
}
});
return;
}

@ -145,10 +145,14 @@ define([
n = n.nThen(function (w) {
setTimeout(w(function () {
el = data[k];
if (!el.href || (el.roHref && false)) {
if (!el.href) {
// Already migrated
return void progress(7, Math.round(100*i/padsLength));
}
if (el.href.indexOf('#') === -1) {
// Encrypted href: already migrated
return void progress(7, Math.round(100*i/padsLength));
}
parsed = Hash.parsePadUrl(el.href);
if (parsed.hashData.type !== "pad") {
// No read-only mode for files

@ -1045,7 +1045,7 @@ define([
if (data.teamId && s.id !== data.teamId) { return; }
if (storeLocally && s.id) { return; }
var res = s.manager.findChannel(channel);
var res = s.manager.findChannel(channel, true);
if (res.length) {
sendTo.push(s.id);
}
@ -1081,7 +1081,7 @@ define([
// If all of the weaker ones were in the trash, add the stronger to ROOT
obj.userObject.restoreHref(href);
}
pad.href = href;
obj.userObject.setHref(channel, null, href);
});
// Pads owned by us ("us" can be a user or a team) that are not in our "main" drive
@ -1474,7 +1474,7 @@ define([
onMetadataUpdate: function (metadata) {
channel.data = metadata || {};
getAllStores().forEach(function (s) {
var allData = s.manager.findChannel(data.channel);
var allData = s.manager.findChannel(data.channel, true);
allData.forEach(function (obj) {
obj.data.owners = metadata.owners;
obj.data.atime = +new Date();
@ -1640,7 +1640,7 @@ define([
// Update owners and expire time in the drive
getAllStores().forEach(function (s) {
var allData = s.manager.findChannel(data.channel);
var allData = s.manager.findChannel(data.channel, true);
var changed = false;
allData.forEach(function (obj) {
if (Sortify(obj.data.owners) !== Sortify(metadata.owners)) {

@ -249,14 +249,13 @@ define([
if (msg.author !== content.user.curvePublic) { return void cb(true); }
var channel = content.channel;
// XXX encrypted href
var res = ctx.store.manager.findChannel(channel);
var res = ctx.store.manager.findChannel(channel, true);
var title;
res.forEach(function (obj) {
if (obj.data && !obj.data.href) {
if (!title) { title = obj.data.filename || obj.data.title; }
obj.data.href = content.href;
obj.userObject.setHref(channel, null, content.href);
}
});

@ -25,7 +25,9 @@ define([
var teamId = store.id || -1;
var handler = store.handleSharedFolder;
var parsed = Hash.parsePadUrl(data.href);
var href = store.manager.user.userObject.getHref(data);
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets('drive', parsed.hash, data.password);
var secondaryKey = secret.keys.secondaryKey;

@ -31,13 +31,26 @@ define([
var debug = exp.debug;
exp.setHref = function (channel, id, href) {
if (!id && !channel) { return; }
var ids = id ? [id] : exp.findChannels([channel]);
ids.forEach(function (i) {
var data = exp.getFileData(i, true);
data.href = exp.cryptor.encrypt(href);
});
};
exp.setPadAttribute = function (href, attr, value, cb) {
cb = cb || function () {};
var id = exp.getIdFromHref(href);
if (!id) { return void cb("E_INVAL_HREF"); }
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
var data = exp.getFileData(id, true);
if (attr === "href") {
exp.setHref(null, id, value);
} else {
data[attr] = clone(value);
}
cb(null);
};
exp.getPadAttribute = function (href, attr, cb) {
@ -51,6 +64,8 @@ define([
exp.pushData = function (data, cb) {
if (typeof cb !== "function") { cb = function () {}; }
var id = Util.createRandomInteger();
// If we were given an edit link, encrypt its value if needed
if (data.href) { data.href = exp.cryptor.encrypt(data.href); }
files[FILES_DATA][id] = data;
cb(null, id);
};
@ -70,6 +85,7 @@ define([
return void cb("EAUTH");
}
var id = Util.createRandomInteger();
if (data.href) { data.href = exp.cryptor.encrypt(data.href); }
files[SHARED_FOLDERS][id] = data;
cb(null, id);
};
@ -209,11 +225,15 @@ define([
id = Number(id);
// Find and maybe update existing pads with the same channel id
var d = data[id];
// If we were given an edit link, encrypt its value if needed
if (d.href) { d.href = exp.cryptor.encrypt(d.href); }
var found = false;
for (var i in files[FILES_DATA]) {
if (files[FILES_DATA][i].channel === d.channel) {
// Update href?
if (!files[FILES_DATA][i].href) { files[FILES_DATA][i].href = d.href; }
if (!files[FILES_DATA][i].href) {
files[FILES_DATA][i].href = d.href;
}
found = true;
break;
}
@ -222,7 +242,7 @@ define([
toRemove.push(id);
return;
}
files[FILES_DATA][id] = data[id];
files[FILES_DATA][id] = d;
});
// Remove existing pads from the "element" variable
@ -500,6 +520,7 @@ define([
if (silent) { debug = function () {}; }
var t0 = +new Date();
debug("Cleaning file system...");
var before = JSON.stringify(files);
@ -536,7 +557,10 @@ define([
// We have an old file (href) which is not in filesData: add it
var id = Util.createRandomInteger();
var key = Hash.createChannelId();
files[FILES_DATA][id] = {href: element[el], filename: el};
files[FILES_DATA][id] = {
href: exp.cryptor.encrypt(element[el]),
filename: el
};
element[key] = id;
delete element[el];
}
@ -562,7 +586,10 @@ define([
if (typeof obj.element === "string") {
// We have an old file (href) which is not in filesData: add it
var id = Util.createRandomInteger();
files[FILES_DATA][id] = {href: obj.element, filename: el};
files[FILES_DATA][id] = {
href: exp.cryptor.encrypt(obj.element),
filename: el
};
obj.element = id;
}
if (exp.isFolder(obj.element)) { fixRoot(obj.element); }
@ -607,7 +634,9 @@ define([
if (typeof el === "string") {
// We have an old file (href) which is not in filesData: add it
var id = Util.createRandomInteger();
files[FILES_DATA][id] = {href: el};
files[FILES_DATA][id] = {
href: exp.cryptor.encrypt(el)
};
us[idx] = id;
}
if (typeof el === "number") {
@ -653,7 +682,12 @@ define([
continue;
}
var parsed = Hash.parsePadUrl(el.href || el.roHref);
var href;
try {
href = el.href && ((el.href.indexOf('#') !== -1) ? el.href : exp.cryptor.decrypt(el.href));
} catch (e) {}
var parsed = Hash.parsePadUrl(href || el.roHref);
var secret;
// Clean invalid hash
@ -670,9 +704,9 @@ define([
}
// If we have an edit link, check the view link
if (el.href && parsed.hashData.type === "pad" && parsed.hashData.version) {
if (href && parsed.hashData.type === "pad" && parsed.hashData.version) {
if (parsed.hashData.mode === "view") {
el.roHref = el.href;
el.roHref = href;
delete el.href;
} else if (!el.roHref) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
@ -691,7 +725,7 @@ define([
}
// Fix href
if (el.href && /^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
if (href && href.slice(0,1) !== '/') { el.href = exp.cryptor.encrypt(Hash.getRelativeHref(el.href)); }
// Fix creation time
if (!el.ctime) { el.ctime = el.atime; }
// Fix title
@ -732,8 +766,13 @@ define([
el = sf[id];
id = Number(id);
var href;
try {
href = el.href && ((el.href.indexOf('#') !== -1) ? el.href : exp.cryptor.decrypt(el.href));
} catch (e) {}
// Fix undefined hash
parsed = Hash.parsePadUrl(el.href || el.roHref);
parsed = Hash.parsePadUrl(href || el.roHref);
secret = Hash.getSecrets('drive', parsed.hash, el.password);
if (!secret.keys) {
delete sf[id];
@ -762,11 +801,12 @@ define([
fixDrive();
fixSharedFolders();
var ms = (+new Date() - t0) + 'ms';
if (JSON.stringify(files) !== before) {
debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely");
debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely.", ms);
return;
}
debug("File system was clean");
debug("File system was clean.", ms);
};
return exp;

@ -81,11 +81,13 @@ define([
// Return files data objects associated to a channel for setPadTitle
// All occurences are returned, in drive or shared folders
var findChannel = function (Env, channel) {
// If "editable" is true, the data returned is a proxy, otherwise
// it's a cloned object (NOTE: href should never be edited directly)
var findChannel = function (Env, channel, editable) {
var ret = [];
Env.user.userObject.findChannels([channel]).forEach(function (id) {
ret.push({
data: Env.user.userObject.getFileData(id),
data: Env.user.userObject.getFileData(id, editable),
userObject: Env.user.userObject
});
});
@ -93,7 +95,7 @@ define([
Env.folders[fId].userObject.findChannels([channel]).forEach(function (id) {
ret.push({
fId: fId,
data: Env.folders[fId].userObject.getFileData(id),
data: Env.folders[fId].userObject.getFileData(id, editable),
userObject: Env.folders[fId].userObject
});
});
@ -101,6 +103,8 @@ define([
return ret;
};
// Return files data objects associated to a given href for setPadAttribute...
// If "editable" is true, the data returned is a proxy, otherwise
// it's a cloned object (NOTE: href should never be edited directly)
var findHref = function (Env, href) {
var ret = [];
var id = Env.user.userObject.getIdFromHref(href);
@ -155,11 +159,11 @@ define([
return ret;
};
var _getFileData = function (Env, id) {
var _getFileData = function (Env, id, editable) {
var userObjects = _getUserObjects(Env);
var data = {};
userObjects.some(function (uo) {
data = uo.getFileData(id);
data = uo.getFileData(id, editable);
if (Object.keys(data).length) { return true; }
});
return data;
@ -278,11 +282,6 @@ define([
filesData[f] = userObject.getFileData(f);
});
// TODO RO
// Encrypt or decrypt edit link here
// filesData.forEach(function (d) { d.href = encrypt(d.href); });
data.push({
el: el,
data: filesData,
@ -435,6 +434,7 @@ define([
Env.pinPads([folderData.channel], waitFor());
}).nThen(function (waitFor) {
// 1. add the shared folder to our list of shared folders
// NOTE: pushSharedFolder will encrypt the href directly in the object if needed
Env.user.userObject.pushSharedFolder(folderData, waitFor(function (err, folderId) {
if (err) {
waitFor.abort();
@ -1128,7 +1128,13 @@ define([
if (!Env.folders[id]) { return {}; }
var obj = Env.folders[id].proxy.metadata || {};
for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) {
obj[k] = Env.user.proxy[UserObject.SHARED_FOLDERS][id][k];
var data = JSON.parse(JSON.stringify(Env.user.proxy[UserObject.SHARED_FOLDERS][id][k]));
if (data.href && data.href.indexOf('#') === -1) {
try {
data.href = Env.user.userObject.cryptor.decrypt(data.href);
} catch (e) {}
}
obj[k] = data;
}
return obj;
};

@ -39,7 +39,13 @@ define([
};
if (config.editKey) {
try {
exp.cryptor = Crypto.createEncryptor(config.editKey);
var c = Crypto.createEncryptor(config.editKey);
exp.cryptor.encrypt = function (href) {
// Never encrypt blob href, they are always read-only
if (href.slice(0,7) === '/file/#') { return href; }
return c.encrypt(href);
};
exp.cryptor.decrypt = c.decrypt;
} catch (e) {
console.error(e);
}
@ -106,6 +112,16 @@ define([
return a;
};
var getHref = exp.getHref = function (pad) {
if (pad.href && pad.href.indexOf('#') !== -1) {
return pad.href;
}
if (pad.href) {
return exp.cryptor.decrypt(pad.href);
}
return pad.roHref;
};
var type = function (dat) {
return dat === null? 'null': Array.isArray(dat)?'array': typeof(dat);
};
@ -219,12 +235,23 @@ define([
};
// Get data from AllFiles (Cryptpad_RECENTPADS)
var getFileData = exp.getFileData = function (file, noCopy) {
var getFileData = exp.getFileData = function (file, editable) {
if (!file) { return; }
var data = files[FILES_DATA][file] || {};
if (!noCopy) {
// XXX encrypted href: decrypt or remove "href"
if (!editable) {
data = JSON.parse(JSON.stringify(data));
if (data.href && data.href.indexOf('#') === -1) {
// Encrypted href: decrypt it if we can, otherwise remove it
if (config.editKey) {
try {
data.href = exp.cryptor.decrypt(data.href);
} catch (e) {
delete data.href;
}
} else {
delete data.href;
}
}
}
return data;
};
@ -401,7 +428,7 @@ define([
var getIdFromHref = exp.getIdFromHref = function (href) {
var result;
getFiles([FILES_DATA]).some(function (id) {
if (files[FILES_DATA][id].href === href ||
if (getHref(files[FILES_DATA][id]) === href ||
files[FILES_DATA][id].roHref === href) {
result = id;
return true;
@ -413,7 +440,7 @@ define([
exp.getSFIdFromHref = function (href) {
var result;
getFiles([SHARED_FOLDERS]).some(function (id) {
if (files[SHARED_FOLDERS][id].href === href ||
if (getHref(files[SHARED_FOLDERS][id]) === href ||
files[SHARED_FOLDERS][id].roHref === href) {
result = id;
return true;

@ -97,7 +97,7 @@ define([
for (var i = 0; i<old.length; i++) {
try {
pad = old[i];
href = pad.href || pad.roHref;
href = (pad.href && pad.href.indexOf('#') !== -1) ? pad.href : pad.roHref;
chan = channelByHref[href];
if (!chan && href) {
parsed = Hash.parsePadUrl(href);
@ -121,7 +121,7 @@ define([
for (var id in ids) {
try {
pad = ids[id];
href = pad.href || pad.roHref;
href = (pad.href && pad.href.indexOf('#') !== -1) ? pad.href : pad.roHref;
chan = pad.channel || channelByHref[href];
if (!chan) {
if (href) {

@ -43,7 +43,7 @@ define([
var oldIds = Object.keys(folders);
nThen(function (waitFor) {
Object.keys(drive.sharedFolders).forEach(function (fId) {
var sfData = drive.sharedFolders[id] || {};
var sfData = drive.sharedFolders[fId] || {};
var parsed = Hash.parsePadUrl(sfData.href);
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
sframeChan.query('Q_DRIVE_GETOBJECT', {

@ -958,12 +958,12 @@ define([
$(list).appendTo(errors);
errs.forEach(function (err) {
if (!err.data) { return; }
var href = err.data.href || err.data.roHref;
var href = (err.data.href && err.data.href.indexOf('#') !== -1) ? err.data.href : err.data.roHref;
$(h('div', [
h('div.title', err.data.filename || err.data.title),
h('div.link', [
h('a', {
href: err.data.href || err.data.roHref,
href: href,
target: '_blank'
}, privateData.origin + href)
]),

@ -52,7 +52,7 @@ define([
var oldIds = Object.keys(folders);
nThen(function (waitFor) {
Object.keys(drive.sharedFolders).forEach(function (fId) {
var sfData = drive.sharedFolders[id] || {};
var sfData = drive.sharedFolders[fId] || {};
var parsed = Hash.parsePadUrl(sfData.href);
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
sframeChan.query('Q_DRIVE_GETOBJECT', {

Loading…
Cancel
Save