From c64eabc33a297e653799288fd292c77622a73ae8 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 30 Oct 2019 18:29:33 +0100 Subject: [PATCH] Change blob password --- rpc.js | 2 + www/common/common-ui-elements.js | 15 ++- www/common/cryptpad-common.js | 105 +++++++++++++++-- www/common/outer/upload.js | 181 +++++++++++++++++------------- www/common/sframe-common-outer.js | 15 +++ 5 files changed, 228 insertions(+), 90 deletions(-) diff --git a/rpc.js b/rpc.js index d4bd41c9c..c3d44e86c 100644 --- a/rpc.js +++ b/rpc.js @@ -857,6 +857,7 @@ var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) { if (err) { return void cb("E_PROOF_REMOVAL"); } + cb(); }); } @@ -869,6 +870,7 @@ var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) { if (err) { return void cb("E_PROOF_REMOVAL"); } + cb(); }); }); } diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 1ee5a9608..7a5eb0c1c 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -79,8 +79,10 @@ define([ waitFor.abort(); return void cb(err || 'EEMPTY'); } - delete val.owners; - delete val.expire; + if (!val.fileType) { + delete val.owners; + delete val.expire; + } Util.extend(data, val); if (data.href) { data.href = base + data.href; } if (data.roHref) { data.roHref = base + data.roHref; } @@ -549,11 +551,12 @@ define([ $d.append(password); } - if (!data.noEditPassword && owned && parsed.hashData.type === 'pad' && parsed.type !== "sheet") { // FIXME SHEET fix password change for sheets + if (!data.noEditPassword && owned && parsed.type !== "sheet") { // FIXME SHEET fix password change for sheets var sframeChan = common.getSframeChannel(); var changePwTitle = Messages.properties_changePassword; var changePwConfirm = Messages.properties_confirmChange; var isSharedFolder = parsed.type === 'drive'; + var isFile = parsed.hashData.type === 'file'; if (!hasPassword) { changePwTitle = Messages.properties_addPassword; changePwConfirm = Messages.properties_confirmNew; @@ -581,7 +584,8 @@ define([ UI.confirm(changePwConfirm, function (yes) { if (!yes) { pLocked = false; return; } $(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'})); - sframeChan.query("Q_PAD_PASSWORD_CHANGE", { + var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE'; + sframeChan.query(q, { teamId: typeof(owned) !== "boolean" ? owned : undefined, href: data.href || data.roHref, password: newPass @@ -593,6 +597,9 @@ define([ return void UI.alert(Messages.properties_passwordError); } UI.findOKButton().click(); + if (isFile) { + return void UI.alert(Messages.properties_passwordSuccess); + } // If we didn't have a password, we have to add the /p/ // If we had a password and we changed it to a new one, we just have to reload // If we had a password and we removed it, we have to remove the /p/ diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 0f0f73d9b..25a64404d 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1023,7 +1023,7 @@ define([ }); }; - common.changeBlobPassword = function (Crypt, Crypto, data, cb) { + common.changeBlobPassword = function (data, handlers, cb) { var href = data.href; var newPassword = data.password; var teamId = data.teamId; @@ -1032,26 +1032,34 @@ define([ if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); } if (parsed.hashData.type !== 'file') { return void cb({ error: 'EINVAL_TYPE' }); } + var newSecret; + var newHash; + if (parsed.hashData.version >= 2) { newSecret = Hash.getSecrets(parsed.type, parsed.hash, newPassword); - if (!(newSecret.keys && newSecret.keys.editKeyStr)) { + if (!(newSecret.keys && newSecret.keys.fileKeyStr)) { return void cb({error: 'EAUTH'}); } - newHash = Hash.getEditHashFromKeys(newSecret); + newHash = Hash.getFileHashFromKeys(newSecret); } else { newHash = Hash.createRandomHash(parsed.type, newPassword); newSecret = Hash.getSecrets(parsed.type, newHash, newPassword); } var newHref = '/' + parsed.type + '/#' + newHash; + var fileHost = Config.fileHost || window.location.origin || ''; /* 1. get old password 2. get owners */ var oldPassword; - var oldSecret; + var decrypted; var oldChannel; - var oldMetadata; + var warning; + + var FileCrypto; + var MediaTag; + var Upload; Nthen(function (waitFor) { if (parsed.hashData && parsed.hashData.password) { common.getPadAttribute('password', waitFor(function (err, password) { @@ -1059,12 +1067,91 @@ define([ }), href); } }).nThen(function (waitFor) { - oldSecret = Hash.getSecrets(parsed.type, parsed.hash, optsGet.password); - oldChannel = oldSecret.channel; - common.getPadMetadata({channel: oldChannel}, waitFor(function (metadata) { - oldMetadata = metadata; + require([ + '/file/file-crypto.js', + '/common/media-tag.js', + '/common/outer/upload.js', + '/bower_components/tweetnacl/nacl-fast.min.js' + ], waitFor(function (_FileCrypto, _MT, _Upload) { + FileCrypto = _FileCrypto; + MediaTag = _MT; + Upload = _Upload; })); }).nThen(function (waitFor) { + var oldSecret = Hash.getSecrets(parsed.type, parsed.hash, oldPassword); + oldChannel = oldSecret.channel; + var src = fileHost + Hash.getBlobPathFromHex(oldChannel); + var key = oldSecret.keys && oldSecret.keys.cryptKey; + var cryptKey = window.nacl.util.encodeBase64(key); + + var mt = document.createElement('media-tag'); + mt.setAttribute('src', src); + mt.setAttribute('data-crypto-key', 'cryptpad:'+cryptKey); + + MediaTag(mt).on('complete', waitFor(function (_decrypted) { + decrypted = _decrypted; + })).on('error', function (err) { + waitFor.abort(); + cb({error: err}); + console.error(err); + }); + }).nThen(function (waitFor) { + var reader = new FileReader(); + reader.readAsArrayBuffer(decrypted.content); + reader.onloadend = waitFor(function() { + decrypted.u8 = new Uint8Array(reader.result); + }); + }).nThen(function (waitFor) { + var key = newSecret.keys && newSecret.keys.cryptKey; + + var onError = function (err) { + waitFor.abort(); + cb({error: err}); + }; + Upload.uploadU8(common, { + teamId: teamId, + u8: decrypted.u8, + metadata: decrypted.metadata, + key: key, + id: newSecret.channel, + owned: true, + onError: onError, + onPending: handlers.onPending, + updateProgress: handlers.updateProgress, + }, waitFor()); + }).nThen(function (waitFor) { + // Set the new password to our pad data + common.setPadAttribute('password', newPassword, waitFor(function (err) { + if (err) { warning = true; } + }), href); + common.setPadAttribute('channel', newSecret.channel, waitFor(function (err) { + if (err) { warning = true; } + }), href); + if (parsed.hashData.password && newPassword) { return; } // same hash + common.setPadAttribute('href', newHref, waitFor(function (err) { + if (err) { warning = true; } + }), href); + }).nThen(function (waitFor) { + // delete the old pad + common.removeOwnedChannel({ + channel: oldChannel, + teamId: teamId + }, waitFor(function (obj) { + if (obj && obj.error) { + waitFor.abort(); + return void cb(obj); + } + })); + postMessage("CHANGE_PAD_PASSWORD_PIN", { + oldChannel: oldChannel, + channel: newSecret.channel + }, waitFor()); + }).nThen(function () { + cb({ + warning: warning, + hash: newHash, + href: newHref, + }); }); }; diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index a443a6d05..62f305612 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -7,6 +7,82 @@ define([ var Nacl = window.nacl; var module = {}; + module.uploadU8 =function (common, data, cb) { + var teamId = data.teamId; + var u8 = data.u8; + var metadata = data.metadata; + var key = data.key; + + var onError = data.onError || function () {}; + var onPending = data.onPending || function () {}; + var updateProgress = data.updateProgress || function () {}; + var owned = data.owned; + var id = data.id; + + var next = FileCrypto.encrypt(u8, metadata, key); + + var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata); + + var sendChunk = function (box, cb) { + var enc = Nacl.util.encodeBase64(box); + common.uploadChunk(teamId, enc, function (e, msg) { + cb(e, msg); + }); + }; + + var actual = 0; + var again = function (err, box) { + if (err) { onError(err); } + if (box) { + actual += box.length; + var progressValue = (actual / estimate * 100); + progressValue = Math.min(progressValue, 100); + updateProgress(progressValue); + + return void sendChunk(box, function (e) { + if (e) { return console.error(e); } + next(again); + }); + } + + if (actual !== estimate) { + console.error('Estimated size does not match actual size'); + } + + // if not box then done + common.uploadComplete(teamId, id, owned, function (e) { + 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); + + + + cb(); + }); + }; + + common.uploadStatus(teamId, estimate, function (e, pending) { + if (e) { + console.error(e); + onError(e); + return; + } + + if (pending) { + return void onPending(function () { + // if the user wants to cancel the pending upload to execute that one + common.uploadCancel(teamId, estimate, function (e) { + if (e) { + return void console.error(e); + } + next(again); + }); + }); + } + next(again); + }); + }; + module.upload = function (file, noStore, common, updateProgress, onComplete, onError, onPending) { var u8 = file.blob; // This is not a blob but a uint8array var metadata = file.metadata; @@ -50,85 +126,36 @@ define([ metadata.owners = [edPublic]; })); }).nThen(function () { - var next = FileCrypto.encrypt(u8, metadata, key); + module.uploadU8(common, { + teamId: teamId, + u8: u8, + metadata: metadata, + key: key, + id: id, + owned: owned, + onError: onError, + onPending: onPending, + updateProgress: updateProgress, + }, function () { + if (noStore) { return void onComplete(href); } - var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata); - - var sendChunk = function (box, cb) { - var enc = Nacl.util.encodeBase64(box); - common.uploadChunk(teamId, enc, function (e, msg) { - cb(e, msg); + var title = metadata.name; + var data = { + teamId: teamId, + title: title || "", + href: href, + path: path, + password: password, + channel: id, + owners: metadata.owners, + forceSave: forceSave + }; + common.setPadTitle(data, function (err) { + if (err) { return void console.error(err); } + onComplete(href); + common.setPadAttribute('fileType', metadata.type, null, href); + common.setPadAttribute('owners', metadata.owners, null, href); }); - }; - - var actual = 0; - var again = function (err, box) { - if (err) { throw new Error(err); } - if (box) { - actual += box.length; - var progressValue = (actual / estimate * 100); - progressValue = Math.min(progressValue, 100); - updateProgress(progressValue); - - return void sendChunk(box, function (e) { - if (e) { return console.error(e); } - next(again); - }); - } - - if (actual !== estimate) { - console.error('Estimated size does not match actual size'); - } - - // if not box then done - common.uploadComplete(teamId, id, owned, function (e) { - 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 title = metadata.name; - - if (noStore) { return void onComplete(href); } - - var data = { - teamId: teamId, - title: title || "", - href: href, - path: path, - password: password, - channel: id, - owners: metadata.owners, - forceSave: forceSave - }; - common.setPadTitle(data, function (err) { - if (err) { return void console.error(err); } - onComplete(href); - common.setPadAttribute('fileType', metadata.type, null, href); - common.setPadAttribute('owners', metadata.owners, null, href); - }); - }); - }; - - common.uploadStatus(teamId, estimate, function (e, pending) { - if (e) { - console.error(e); - onError(e); - return; - } - - if (pending) { - return void onPending(function () { - // if the user wants to cancel the pending upload to execute that one - common.uploadCancel(teamId, estimate, function (e) { - if (e) { - return void console.error(e); - } - next(again); - }); - }); - } - next(again); }); }); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index d8aa64162..e4222a4ad 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -991,6 +991,21 @@ define([ }); }); + sframeChan.on('Q_BLOB_PASSWORD_CHANGE', function (data, cb) { + data.href = data.href || window.location.href; + var onPending = function () { + // XXX + }; + var updateProgress = function (p) { + // XXX + console.log(p); + }; + Cryptpad.changeBlobPassword(data, { + onPending: onPending, + updateProgress: updateProgress + }, cb); + }); + sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) { data.href = data.href || window.location.href; Cryptpad.changePadPassword(Cryptget, Crypto, data, cb);