diff --git a/www/code/inner.js b/www/code/inner.js
index 0a6b11abc..afb4cdcd0 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -331,14 +331,11 @@ define([
dropArea: $('.CodeMirror'),
body: $('body'),
onUploaded: function (ev, data) {
- //var cursor = editor.getCursor();
- //var cleanName = data.name.replace(/[\[\]]/g, '');
- //var text = '!['+cleanName+']('+data.url+')';
- // PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
- var hexFileName = Util.base64ToHex(parsed.hashData.channel);
- var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
- var mt = '';
+ var secret = Hash.getSecrets('file', parsed.hash, data.password);
+ var src = Hash.getBlobPathFromHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys.cryptKey);
+ var mt = '';
editor.replaceSelection(mt);
}
};
diff --git a/www/common/common-hash.js b/www/common/common-hash.js
index 3aaa7719b..2b75b0e8e 100644
--- a/www/common/common-hash.js
+++ b/www/common/common-hash.js
@@ -11,6 +11,7 @@ define([
var uint8ArrayToHex = Util.uint8ArrayToHex;
var hexToBase64 = Util.hexToBase64;
var base64ToHex = Util.base64ToHex;
+ Hash.encodeBase64 = Nacl.util.encodeBase64;
// This implementation must match that on the server
// it's used for a checksum
@@ -59,6 +60,11 @@ define([
return '/1/' + hexToBase64(secret.channel) + '/' +
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
}
+ if (version === 2) {
+ if (!data.fileKeyStr) { return; }
+ var pass = secret.password ? 'p/' : '';
+ return '/2/' + secret.type + '/' + Crypto.b64RemoveSlashes(data.fileKeyStr) + '/' + pass;
+ }
};
// V1
@@ -95,12 +101,22 @@ define([
};
Hash.createRandomHash = function (type, password) {
- var cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
+ var cryptor;
+ if (type === 'file') {
+ cryptor = Crypto.createFileCryptor2(void 0, password);
+ return getFileHashFromKeys({
+ password: Boolean(password),
+ version: 2,
+ type: type,
+ keys: cryptor.fileKeyStr
+ });
+ }
+ cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
return getEditHashFromKeys({
password: Boolean(password),
version: 2,
type: type,
- keys: { editKeyStr: cryptor.editKeyStr }
+ keys: cryptor.editKeyStr
});
};
@@ -113,6 +129,7 @@ Version 1
var parseTypeHash = Hash.parseTypeHash = function (type, hash) {
if (!hash) { return; }
+ var options;
var parsed = {};
var hashArr = fixDuplicateSlashes(hash).split('/');
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
@@ -125,7 +142,6 @@ Version 1
parsed.version = 0;
return parsed;
}
- var options;
if (hashArr[1] && hashArr[1] === '1') { // Version 1
parsed.version = 1;
parsed.mode = hashArr[2];
@@ -175,6 +191,25 @@ Version 1
parsed.key = hashArr[3].replace(/-/g, '/');
return parsed;
}
+ if (hashArr[1] && hashArr[1] === '2') { // Version 2
+ parsed.version = 2;
+ parsed.app = hashArr[2];
+ parsed.key = hashArr[3];
+
+ options = hashArr.slice(4);
+ parsed.password = options.indexOf('p') !== -1;
+ parsed.present = options.indexOf('present') !== -1;
+ parsed.embed = options.indexOf('embed') !== -1;
+
+ parsed.getHash = function (opts) {
+ var hash = hashArr.slice(0, 4).join('/') + '/';
+ if (parsed.password) { hash += 'p/'; }
+ if (opts.embed) { hash += 'embed/'; }
+ if (opts.present) { hash += 'present/'; }
+ return hash;
+ };
+ return parsed;
+ }
return parsed;
}
if (['user'].indexOf(type) !== -1) {
@@ -309,11 +344,12 @@ Version 1
}
}
} else if (parsed.type === "file") {
- // version 2 hashes are to be used for encrypted blobs
- secret.channel = parsed.channel;
- secret.keys = { fileKeyStr: parsed.key };
+ secret.channel = base64ToHex(parsed.channel);
+ secret.keys = {
+ fileKeyStr: parsed.key,
+ cryptKey: Nacl.util.decodeBase64(parsed.key)
+ };
} else if (parsed.type === "user") {
- // version 2 hashes are to be used for encrypted blobs
throw new Error("User hashes can't be opened (yet)");
}
} else if (parsed.version === 2) {
@@ -338,7 +374,12 @@ Version 1
}
}
} else if (parsed.type === "file") {
- throw new Error("File hashes should be version 1");
+ secret.channel = base64ToHex(secret.keys.chanId);
+ secret.keys = Crypto.createFileCryptor2(parsed.key, password);
+ secret.key = secret.keys.fileKeyStr;
+ if (secret.channel.length !== 48 || secret.key.length !== 24) {
+ throw new Error("The channel key and/or the encryption key is invalid");
+ }
} else if (parsed.type === "user") {
throw new Error("User hashes can't be opened (yet)");
}
diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js
index ab6d3e6d4..bc8145d46 100644
--- a/www/common/common-thumbnail.js
+++ b/www/common/common-thumbnail.js
@@ -250,17 +250,15 @@ define([
var k = getKey(parsed.type, channel);
common.setThumbnail(k, b64, cb);
};
- Thumb.displayThumbnail = function (common, href, channel, $container, cb) {
+ Thumb.displayThumbnail = function (common, href, channel, password, $container, cb) {
cb = cb || function () {};
var parsed = Hash.parsePadUrl(href);
var k = getKey(parsed.type, channel);
var whenNewThumb = function () {
- // PASSWORD_FILES
- var secret = Hash.getSecrets('file', parsed.hash);
- var hexFileName = Util.base64ToHex(secret.channel);
+ var secret = Hash.getSecrets('file', parsed.hash, password);
+ var hexFileName = channel;
var src = Hash.getBlobPathFromHex(hexFileName);
- var cryptKey = secret.keys && secret.keys.fileKeyStr;
- var key = Nacl.util.decodeBase64(cryptKey);
+ var key = secret.keys && secret.keys.cryptKey;
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
if (e) {
if (e === 'XHR_ERROR') { return; }
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 3ba800b49..2169e1640 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -1169,8 +1169,8 @@ define([
// No password for avatars
var secret = Hash.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) {
- var cryptKey = secret.keys && secret.keys.fileKeyStr;
- var hexFileName = Util.base64ToHex(secret.channel);
+ var hexFileName = secret.channel;
+ var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
var src = Hash.getBlobPathFromHex(hexFileName);
Common.getFileSize(hexFileName, function (e, data) {
if (e || !data) {
diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 5faffc5c9..68debf694 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -578,7 +578,6 @@ define([
}
var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
- if (parsed.type === 'file' && typeof(parsed.channel) === 'string') { secret.channel = Util.base64ToHex(secret.channel); }
hashes = Hash.getHashes(secret);
if (secret.version === 0) {
diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js
index 80c9c4b79..fca3023b4 100644
--- a/www/common/diffMarked.js
+++ b/www/common/diffMarked.js
@@ -41,11 +41,15 @@ define([
};
renderer.image = function (href, title, text) {
if (href.slice(0,6) === '/file/') {
- // PASSWORD_FILES
+ // DEPRECATED
+ // Mediatag using markdown syntax should not be used anymore so they don't support
+ // password-protected files
+ console.log('DEPRECATED: mediatag using markdown syntax!');
var parsed = Hash.parsePadUrl(href);
- var hexFileName = Util.base64ToHex(parsed.hashData.channel);
- var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
- var mt = '';
+ var secret = Hash.getSecrets('file', parsed.hash);
+ var src = Hash.getBlobPathFromHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys.cryptKey);
+ var mt = '';
if (mediaMap[src]) {
mt += mediaMap[src];
}
diff --git a/www/common/migrate-user-object.js b/www/common/migrate-user-object.js
index 1d6dd1210..fb69fb20b 100644
--- a/www/common/migrate-user-object.js
+++ b/www/common/migrate-user-object.js
@@ -115,13 +115,8 @@ define([
parsed = Hash.parsePadUrl(el.href);
if (!el.href) { return; }
if (!el.channel) {
- if (parsed.hashData && parsed.hashData.type === "file") {
- // PASSWORD_FILES
- el.channel = Util.base64ToHex(parsed.hashData.channel);
- } else {
- var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
- el.channel = secret.channel;
- }
+ var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
+ el.channel = secret.channel;
progress(6, Math.round(100*i/padsLength));
console.log('Adding missing channel in filesData ', el.channel);
}
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index e02a527ae..20cea6cd0 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -92,7 +92,7 @@ define([
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
if (profileChan) { list.push(profileChan); }
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
- if (avatarChan) { list.push(Util.base64ToHex(avatarChan)); }
+ if (avatarChan) { list.push(avatarChan); }
}
if (store.proxy.friends) {
diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js
index 7f3874511..9ccc32500 100644
--- a/www/common/outer/upload.js
+++ b/www/common/outer/upload.js
@@ -13,7 +13,16 @@ define([
// if it exists, path contains the new pad location in the drive
var path = file.path;
- var key = Nacl.randomBytes(32);
+ // XXX
+ // PASSWORD_FILES
+ var password;
+ var hash = Hash.createRandomHash('file', password);
+ var secret = Hash.getSecrets('file', hash, password);
+ var key = secret.keys.cryptKey;
+ var id = secret.channel;
+ //var key = Nacl.randomBytes(32);
+
+ // XXX provide channel id to "next"
var next = FileCrypto.encrypt(u8, metadata, key);
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
@@ -44,21 +53,11 @@ define([
}
// if not box then done
- common.uploadComplete(function (e, id) {
+ common.uploadComplete(function (e/*, id*/) { // XXX id is given, not asked
if (e) { return void console.error(e); }
var uri = ['', 'blob', id.slice(0,2), id].join('/');
console.log("encrypted blob is now available as %s", uri);
- var b64Key = Nacl.util.encodeBase64(key);
-
- var secret = {
- version: 1,
- channel: id,
- keys: {
- fileKeyStr: b64Key
- }
- };
- var hash = Hash.getFileHashFromKeys(secret);
var href = '/file/#' + hash;
var title = metadata.name;
diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js
index b24092673..8b2270797 100644
--- a/www/common/outer/userObject.js
+++ b/www/common/outer/userObject.js
@@ -589,14 +589,9 @@ define([
// Fix channel
if (!el.channel) {
try {
- if (parsed.hashData && parsed.hashData.type === "file") {
- // PASSWORD_FILES
- el.channel = Util.base64ToHex(parsed.hashData.channel);
- } else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.channel = secret.channel;
- }
- console.log('Adding missing channel in filesData ', el.channel);
+ console.log('Adding missing channel in filesData ', el.channel);
} catch (e) {
console.error(e);
}
diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js
index 19b4c7ae0..0c1a6a7a5 100644
--- a/www/common/sframe-common-codemirror.js
+++ b/www/common/sframe-common-codemirror.js
@@ -329,15 +329,11 @@ define([
dropArea: $('.CodeMirror'),
body: $('body'),
onUploaded: function (ev, data) {
- //var cursor = editor.getCursor();
- //var cleanName = data.name.replace(/[\[\]]/g, '');
- //var text = '!['+cleanName+']('+data.url+')';
- // PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
- var hexFileName = Util.base64ToHex(parsed.hashData.channel);
- var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
- var mt = '';
+ var secret = Hash.getSecrets('file', parsed.hash, data.password);
+ var src = Hash.getBlobPathFromHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys.cryptKey);
+ var mt = '';
editor.replaceSelection(mt);
}
};
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index 4fc5a6ac6..572fe5f4a 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -113,16 +113,16 @@ define([
return '';
};
funcs.getMediatagFromHref = function (href) {
- // PASSWORD_FILES
- var parsed = Hash.parsePadUrl(href);
- var secret = Hash.getSecrets('file', parsed.hash);
+ // XXX: Should only be used with the current href
var data = ctx.metadataMgr.getPrivateData();
+ var parsed = Hash.parsePadUrl(href);
+ var secret = Hash.getSecrets('file', parsed.hash, data.password);
if (secret.keys && secret.channel) {
- var cryptKey = secret.keys && secret.keys.fileKeyStr;
- var hexFileName = Util.base64ToHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
+ var hexFileName = secret.channel;
var origin = data.fileHost || data.origin;
var src = origin + Hash.getBlobPathFromHex(hexFileName);
- return '' +
+ return '' +
'';
}
return;
diff --git a/www/common/userObject.js b/www/common/userObject.js
index b05798946..fb39eb484 100644
--- a/www/common/userObject.js
+++ b/www/common/userObject.js
@@ -590,7 +590,6 @@ define([
}
}, cb);
}
- console.log(path, newName);
if (path.length <= 1) {
logError('Renaming `root` is forbidden');
return;
diff --git a/www/drive/inner.js b/www/drive/inner.js
index b668429cd..d072992d6 100644
--- a/www/drive/inner.js
+++ b/www/drive/inner.js
@@ -1305,7 +1305,7 @@ define([
$span.attr('title', name);
var type = Messages.type[hrefData.type] || hrefData.type;
- common.displayThumbnail(data.href, data.channel, $span, function ($thumb) {
+ common.displayThumbnail(data.href, data.channel, data.password, $span, function ($thumb) {
// Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in
// list mode too
diff --git a/www/file/inner.js b/www/file/inner.js
index 3d752d95e..d472cb95d 100644
--- a/www/file/inner.js
+++ b/www/file/inner.js
@@ -54,17 +54,14 @@ define([
var uploadMode = false;
var secret;
- var hexFileName;
var metadataMgr = common.getMetadataMgr();
var priv = metadataMgr.getPrivateData();
if (!priv.filehash) {
uploadMode = true;
} else {
- // PASSWORD_FILES
- secret = Hash.getSecrets('file', priv.filehash);
+ secret = Hash.getSecrets('file', priv.filehash, priv.password);
if (!secret.keys) { throw new Error("You need a hash"); }
- hexFileName = Util.base64ToHex(secret.channel);
}
var Title = common.createTitle({});
@@ -87,9 +84,10 @@ define([
toolbar.$rightside.html('');
if (!uploadMode) {
+ var hexFileName = secret.channel;
var src = Hash.getBlobPathFromHex(hexFileName);
- var cryptKey = secret.keys && secret.keys.fileKeyStr;
- var key = Nacl.util.decodeBase64(cryptKey);
+ var key = secret.keys && secret.keys.cryptKey;
+ var cryptKey = Nacl.util.encodeBase64(key);
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
if (e) {
@@ -118,9 +116,7 @@ define([
};
var $mt = $dlview.find('media-tag');
- var cryptKey = secret.keys && secret.keys.fileKeyStr;
- var hexFileName = Util.base64ToHex(secret.channel);
- $mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName);
+ $mt.attr('src', src);
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
var rightsideDisplayed = false;
@@ -263,7 +259,7 @@ define([
dropArea: $form,
hoverArea: $label,
body: $body,
- keepTable: true // Don't fadeOut the tbale with the uploaded files
+ keepTable: true // Don't fadeOut the table with the uploaded files
};
var FM = common.createFileManager(fmConfig);
diff --git a/www/filepicker/inner.js b/www/filepicker/inner.js
index 9dd05076a..c30ada772 100644
--- a/www/filepicker/inner.js
+++ b/www/filepicker/inner.js
@@ -40,14 +40,14 @@ define([
var parsed = Hash.parsePadUrl(data.url);
hideFileDialog();
if (parsed.type === 'file') {
- // PASSWORD_FILES
- var hexFileName = Util.base64ToHex(parsed.hashData.channel);
- var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
+ var secret = Hash.getSecrets('file', parsed.hash, data.password);
+ var src = Hash.getBlobPathFromHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys.cryptKey);
sframeChan.event("EV_FILE_PICKED", {
type: parsed.type,
src: src,
name: data.name,
- key: parsed.hashData.key
+ key: key
});
return;
}
@@ -69,8 +69,8 @@ define([
APP.FM = common.createFileManager(fmConfig);
// Create file picker
- var onSelect = function (url, name) {
- onFilePicked({url: url, name: name});
+ var onSelect = function (url, name, password) {
+ onFilePicked({url: url, name: name, password: password});
};
var data = {
FM: APP.FM
@@ -135,11 +135,13 @@ define([
$('', {'class': 'cp-filepicker-content-element-name'}).text(name)
.appendTo($span);
$span.click(function () {
- if (typeof onSelect === "function") { onSelect(data.href, name); }
+ if (typeof onSelect === "function") {
+ onSelect(data.href, name, data.password);
+ }
});
// Add thumbnail if it exists
- common.displayThumbnail(data.href, data.channel, $span);
+ common.displayThumbnail(data.href, data.channel, data.password, $span);
});
$input.focus();
};
diff --git a/www/pad/inner.js b/www/pad/inner.js
index 5b150c419..b8199bc61 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -552,11 +552,11 @@ define([
ckeditor: editor,
body: $('body'),
onUploaded: function (ev, data) {
- // PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
- var hexFileName = Util.base64ToHex(parsed.hashData.channel);
- var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
- var mt = '';
+ var secret = Hash.getSecrets('file', parsed.hash, data.password);
+ var src = Hash.getBlobPathFromHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys.cryptKey);
+ var mt = '';
// MEDIATAG
var element = window.CKEDITOR.dom.element.createFromHtml(mt);
editor.insertElement(element);
diff --git a/www/slide/inner.js b/www/slide/inner.js
index 18b7da4e3..497b0b8ca 100644
--- a/www/slide/inner.js
+++ b/www/slide/inner.js
@@ -500,11 +500,11 @@ define([
dropArea: $('.CodeMirror'),
body: $('body'),
onUploaded: function (ev, data) {
- // PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
- var hexFileName = Util.base64ToHex(parsed.hashData.channel);
- var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
- var mt = '';
+ var secret = Hash.getSecrets('file', parsed.hash, data.password);
+ var src = Hash.getBlobPathFromHex(secret.channel);
+ var key = Hash.encodeBase64(secret.keys.cryptKey);
+ var mt = '';
editor.replaceSelection(mt);
}
};