From 922e7803e99de418d5f4723b955921b720b9d5c5 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 10 May 2017 11:54:05 +0200 Subject: [PATCH 1/6] remove extraneous slashes from hashes when parsing --- www/common/common-hash.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/www/common/common-hash.js b/www/common/common-hash.js index d4c2bb112..a2e96e2fc 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -66,6 +66,10 @@ define([ return '/' + parsed.type + '/#' + parsed.hash; }; + var fixDuplicateSlashes = function (s) { + return s.replace(/\/+/g, '/'); + }; + /* * Returns all needed keys for a realtime channel * - no argument: use the URL hash or create one if it doesn't exist @@ -95,7 +99,7 @@ define([ } else { // New hash - var hashArray = hash.split('/'); + var hashArray = fixDuplicateSlashes(hash).split('/'); if (hashArray.length < 4) { Hash.alert("Unable to parse the key"); throw new Error("Unable to parse the key"); @@ -179,7 +183,7 @@ Version 2 parsed.version = 0; return parsed; } - var hashArr = hash.split('/'); + var hashArr = fixDuplicateSlashes(hash).split('/'); if (hashArr[1] && hashArr[1] === '1') { parsed.version = 1; parsed.mode = hashArr[2]; From 6a1c799a6ea79d7b26eb55ea48800d40f9cc0c69 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 10 May 2017 11:54:21 +0200 Subject: [PATCH 2/6] support async tests --- www/assert/main.js | 130 +++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 53 deletions(-) diff --git a/www/assert/main.js b/www/assert/main.js index 392392db8..449834a61 100644 --- a/www/assert/main.js +++ b/www/assert/main.js @@ -15,26 +15,40 @@ define([ var failMessages = []; var ASSERTS = []; - var runASSERTS = function () { + var runASSERTS = function (cb) { + var count = ASSERTS.length; + var successes = 0; + + var done = function (err, index) { + count--; + if (err) { failMessages.push(err); } + else { successes++; } + if (count === 0) { cb(); } + }; + ASSERTS.forEach(function (f, index) { - f(index); + f(function (err) { + done(err, index); + }, index); }); }; var assert = function (test, msg) { - ASSERTS.push(function (i) { - var returned = test(); - if (returned === true) { - assertions++; - } else { - failed = true; - failedOn = assertions; - failMessages.push({ - test: i, - message: msg, - output: returned, - }); - } + ASSERTS.push(function (cb, i) { + test(function (result) { + if (result === true) { + assertions++; + cb(); + } else { + failed = true; + failedOn = assertions; + cb({ + test: i, + message: msg, + output: result, + }); + } + }); }); }; @@ -58,7 +72,7 @@ define([ }; var HJSON_equal = function (shjson) { - assert(function () { + assert(function (cb) { // parse your stringified Hyperjson var hjson; @@ -82,10 +96,10 @@ define([ var diff = TextPatcher.format(shjson, op); if (success) { - return true; + return cb(true); } else { - return '

insert: ' + diff.insert + '

' + - 'remove: ' + diff.remove + '

'; + return cb('

insert: ' + diff.insert + '

' + + 'remove: ' + diff.remove + '

'); } }, "expected hyperjson equality"); }; @@ -94,7 +108,7 @@ define([ var roundTrip = function (sel) { var target = $(sel)[0]; - assert(function () { + assert(function (cb) { var hjson = Hyperjson.fromDOM(target); var cloned = Hyperjson.toDOM(hjson); var success = cloned.outerHTML === target.outerHTML; @@ -111,7 +125,7 @@ define([ TextPatcher.log(target.outerHTML, op); } - return success; + return cb(success); }, "Round trip serialization introduced artifacts."); }; @@ -125,9 +139,9 @@ define([ var strungJSON = function (orig) { var result; - assert(function () { + assert(function (cb) { result = JSON.stringify(JSON.parse(orig)); - return result === orig; + return cb(result === orig); }, "expected result (" + result + ") to equal original (" + orig + ")"); }; @@ -138,46 +152,56 @@ define([ }); // check that old hashes parse correctly - assert(function () { + assert(function (cb) { var secret = Cryptpad.parseHash('67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy'); - return secret.channel === "67b8385b07352be53e40746d2be6ccd7" && + return cb(secret.channel === "67b8385b07352be53e40746d2be6ccd7" && secret.key === "XAYSuJYYqa9NfmInyHci7LNy" && - secret.version === 0; + secret.version === 0); }, "Old hash failed to parse"); // make sure version 1 hashes parse correctly - assert(function () { + assert(function (cb) { var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI'); - return secret.version === 1 && + return cb(secret.version === 1 && secret.mode === "edit" && secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" && secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" && - !secret.present; + !secret.present); }, "version 1 hash failed to parse"); // test support for present mode in hashes - assert(function () { + assert(function (cb) { var secret = Cryptpad.parseHash('/1/edit/CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present'); - return secret.version === 1 + return cb(secret.version === 1 && secret.mode === "edit" && secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.key === "DNZ2wcG683GscU4fyOyqA87G" - && secret.present; + && secret.present); }, "version 1 hash failed to parse"); + // test support for present mode in hashes + assert(function (cb) { + var secret = Cryptpad.parseHash('/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G//present'); + return cb(secret.version === 1 + && secret.mode === "edit" + && secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg" + && secret.key === "DNZ2wcG683GscU4fyOyqA87G" + && secret.present); + }, "Couldn't handle multiple successive slashes"); + // test support for trailing slash - assert(function () { + assert(function (cb) { var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/'); - return secret.version === 1 && + return cb(secret.version === 1 && secret.mode === "edit" && secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" && secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" && - !secret.present; + !secret.present); }, "test support for trailing slashes in version 1 hash failed to parse"); - assert(function () { + assert(function (cb) { // TODO - return true; + return cb(true); }, "version 2 hash failed to parse correctly"); var swap = function (str, dict) { @@ -194,7 +218,7 @@ define([ return str || ''; }; - var formatFailures = function () { + var formatFailures = function () { var template = multiline(function () { /*

Failed on test number {{test}} with error message: @@ -215,16 +239,15 @@ The test returned: }).join("\n"); }; - runASSERTS(); - - $("body").html(function (i, val) { - var dict = { - previous: val, - totalAssertions: ASSERTS.length, - passedAssertions: assertions, - plural: (assertions === 1? '' : 's'), - failMessages: formatFailures() - }; + runASSERTS(function () { + $("body").html(function (i, val) { + var dict = { + previous: val, + totalAssertions: ASSERTS.length, + passedAssertions: assertions, + plural: (assertions === 1? '' : 's'), + failMessages: formatFailures() + }; var SUCCESS = swap(multiline(function(){/*

{{passedAssertions}} / {{totalAssertions}} test{{plural}} passed. @@ -237,12 +260,13 @@ The test returned: {{previous}} */}), dict); - var report = SUCCESS; + var report = SUCCESS; - return report; - }); + return report; + }); - var $report = $('.report'); - $report.addClass(failed?'failure':'success'); + var $report = $('.report'); + $report.addClass(failed?'failure':'success'); + }); }); From 97a28d3938865e732cd9508cb5b71faffb0ecda9 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 10 May 2017 11:56:10 +0200 Subject: [PATCH 3/6] test file encryption/decryption --- www/file/test/index.html | 16 +++++++ www/file/test/main.js | 97 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 www/file/test/index.html create mode 100644 www/file/test/main.js diff --git a/www/file/test/index.html b/www/file/test/index.html new file mode 100644 index 000000000..75b04dc83 --- /dev/null +++ b/www/file/test/index.html @@ -0,0 +1,16 @@ + + + + CryptPad + + + + + + + + diff --git a/www/file/test/main.js b/www/file/test/main.js new file mode 100644 index 000000000..c2b24e4f2 --- /dev/null +++ b/www/file/test/main.js @@ -0,0 +1,97 @@ +define([ + 'jquery', + '/bower_components/chainpad-crypto/crypto.js', + '/bower_components/chainpad-netflux/chainpad-netflux.js', + '/common/toolbar.js', + '/common/cryptpad-common.js', + '/common/visible.js', + '/common/notify.js', + '/file/file-crypto.js', + '/bower_components/tweetnacl/nacl-fast.min.js', + '/bower_components/file-saver/FileSaver.min.js', +], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) { + var Nacl = window.nacl; + + $(function () { + + var filesAreSame = function (a, b) { + var l = a.length; + if (l !== b.length) { return false; } + + var i = 0; + for (; i < l; i++) { if (a[i] !== b[i]) { return false; } } + return true; + }; + + var metadataIsSame = function (A, B) { + return !Object.keys(A).some(function (k) { + return A[k] !== B[k]; + }); + }; + + var upload = function (blob, metadata) { + var u8 = new Uint8Array(blob); + + var key = Nacl.randomBytes(32); + var next = FileCrypto.encrypt(u8, metadata, key); + + var chunks = []; + var sendChunk = function (box, cb) { + chunks.push(box); + cb(); + }; + + var again = function (state, box) { + switch (state) { + case 0: + sendChunk(box, function (e) { + if (e) { return console.error(e); } + next(again); + }); + break; + case 1: + sendChunk(box, function (e) { + if (e) { return console.error(e); } + next(again); + }); + break; + case 2: + sendChunk(box, function (e) { + if (e) { return console.error(e); } + + // check if the uploaded file can be decrypted + var newU8 = FileCrypto.joinChunks(chunks); + FileCrypto.decrypt(newU8, key, function (e, res) { + if (e) { return Cryptpad.alert(e); } + + if (filesAreSame(blob, res.content) && + metadataIsSame(res.metadata, metadata)) { + Cryptpad.alert("successfully uploaded"); + } else { + Cryptpad.alert('encryption failure!'); + } + }); + }); + break; + default: + throw new Error("E_INVAL_STATE"); + } + }; + next(again); + }; + + var andThen = function () { + var src = '/customize/cryptofist_mini.png'; + Cryptpad.fetch(src, function (e, file) { + console.log(file); + upload(file, { + pew: 'pew', + bang: 'bang', + }); + }); + }; + + andThen(); + + }); +}); From db2f7a5e36d2ab129311c4eb513496cd061d69a0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 10 May 2017 15:36:14 +0200 Subject: [PATCH 4/6] add todos and change file id size --- rpc.js | 92 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/rpc.js b/rpc.js index db3e98faa..ec6e516d0 100644 --- a/rpc.js +++ b/rpc.js @@ -13,7 +13,8 @@ var RPC = module.exports; var Store = require("./storage/file"); var isValidChannel = function (chan) { - return /^[a-fA-F0-9]/.test(chan); + return /^[a-fA-F0-9]/.test(chan) || + [32, 48].indexOf(chan.length) !== -1; }; var uint8ArrayToHex = function (a) { @@ -33,10 +34,10 @@ var uint8ArrayToHex = function (a) { }).join(''); }; -var createChannelId = function () { - var id = uint8ArrayToHex(Nacl.randomBytes(16)); - if (id.length !== 32 || /[^a-f0-9]/.test(id)) { - throw new Error('channel ids must consist of 32 hex characters'); +var createFileId = function () { + var id = uint8ArrayToHex(Nacl.randomBytes(24)); + if (id.length !== 48 || /[^a-f0-9]/.test(id)) { + throw new Error('file ids must consist of 48 hex characters'); } return id; }; @@ -245,14 +246,32 @@ var getChannelList = function (store, Sessions, publicKey, cb) { }); }; +var getUploadSize = function (store, channel, cb) { + var path = ''; + + Fs.stat(path, function (err, stats) { + if (err) { return void cb(err); } + cb(void 0, stats.size); + }); +}; + var getFileSize = function (store, channel, cb) { if (!isValidChannel(channel)) { return void cb('INVALID_CHAN'); } - if (typeof(store.getChannelSize) !== 'function') { - return cb('GET_CHANNEL_SIZE_UNSUPPORTED'); + + if (channel.length === 32) { + if (typeof(store.getChannelSize) !== 'function') { + return cb('GET_CHANNEL_SIZE_UNSUPPORTED'); + } + + return void store.getChannelSize(channel, function (e, size) { + if (e) { return void cb(e.code); } + cb(void 0, size); + }); } - return void store.getChannelSize(channel, function (e, size) { - if (e) { return void cb(e.code); } + // 'channel' refers to a file, so you need anoter API + getUploadSize(null, channel, function (e, size) { + if (e) { return void cb(e); } cb(void 0, size); }); }; @@ -332,6 +351,7 @@ var getHash = function (store, Sessions, publicKey, cb) { store.message(publicKey, JSON.stringify(msg), cb); }; */ +// TODO check if new pinned size exceeds user quota var pinChannel = function (store, Sessions, publicKey, channels, cb) { if (!channels && channels.filter) { // expected array @@ -383,7 +403,7 @@ var unpinChannel = function (store, Sessions, publicKey, channels, cb) { function (e) { if (e) { return void cb(e); } toStore.forEach(function (channel) { - delete session.channels[channel]; // = false; + delete session.channels[channel]; }); getHash(store, Sessions, publicKey, cb); @@ -391,6 +411,7 @@ var unpinChannel = function (store, Sessions, publicKey, channels, cb) { }); }; +// TODO check if new pinned size exceeds user quota var resetUserPins = function (store, Sessions, publicKey, channelList, cb) { var session = beginSession(Sessions, publicKey); @@ -469,13 +490,13 @@ var makeFileStream = function (root, id, cb) { }); }; -var upload = function (stagingPath, Sessions, publicKey, content, cb) { +var upload = function (paths, Sessions, publicKey, content, cb) { var dec = new Buffer(Nacl.util.decodeBase64(content)); // jshint ignore:line var session = Sessions[publicKey]; session.atime = +new Date(); if (!session.blobstage) { - makeFileStream(stagingPath, publicKey, function (e, stream) { + makeFileStream(paths.staging, publicKey, function (e, stream) { if (e) { return void cb(e); } var blobstage = session.blobstage = stream; @@ -488,10 +509,10 @@ var upload = function (stagingPath, Sessions, publicKey, content, cb) { } }; -var upload_cancel = function (stagingPath, Sessions, publicKey, cb) { - var path = makeFilePath(stagingPath, publicKey); +var upload_cancel = function (paths, Sessions, publicKey, cb) { + var path = makeFilePath(paths.staging, publicKey); if (!path) { - console.log(stagingPath, publicKey); + console.log(paths.staging, publicKey); console.log(path); return void cb('NO_FILE'); } @@ -512,7 +533,13 @@ var isFile = function (filePath, cb) { }); }; -var upload_complete = function (stagingPath, storePath, Sessions, publicKey, cb) { +/* TODO +change channel IDs to a different length so that when we pin, we will be able +to tell that it is not a channel, but a file, just by its length. + +also, when your upload is complete, pin the resulting file. +*/ +var upload_complete = function (paths, Sessions, publicKey, cb) { var session = Sessions[publicKey]; if (session.blobstage && session.blobstage.close) { @@ -520,14 +547,14 @@ var upload_complete = function (stagingPath, storePath, Sessions, publicKey, cb) delete session.blobstage; } - var oldPath = makeFilePath(stagingPath, publicKey); + var oldPath = makeFilePath(paths.staging, publicKey); var tryRandomLocation = function (cb) { - var id = createChannelId(); + var id = createFileId(); var prefix = id.slice(0, 2); - var newPath = makeFilePath(storePath, id); + var newPath = makeFilePath(paths.blob, id); - safeMkdir(Path.join(storePath, prefix), function (e) { + safeMkdir(Path.join(paths.blob, prefix), function (e) { if (e) { console.error(e); return void cb('RENAME_ERR'); @@ -558,8 +585,14 @@ var upload_complete = function (stagingPath, storePath, Sessions, publicKey, cb) }); }; -var upload_status = function (stagingPath, Sessions, publicKey, cb) { - var filePath = makeFilePath(stagingPath, publicKey); +/* TODO +when asking about your upload status, also send some information about how big +your upload is going to be. if that would exceed your limit, return TOO_LARGE +error. + +*/ +var upload_status = function (paths, Sessions, publicKey, cb) { + var filePath = makeFilePath(paths.staging, publicKey); if (!filePath) { return void cb('E_INVALID_PATH'); } isFile(filePath, function (e, yes) { cb(e, yes); @@ -577,9 +610,10 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) return typeof(config[key]) === 'string'? config[key]: def; }; - var pinPath = keyOrDefaultString('pinPath', './pins'); - var blobPath = keyOrDefaultString('blobPath', './blob'); - var blobStagingPath = keyOrDefaultString('blobStagingPath', './blobstage'); + var paths = {}; + var pinPath = paths.pin = keyOrDefaultString('pinPath', './pins'); + var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob'); + var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage'); var store; @@ -695,22 +729,22 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) // restricted to privileged users... case 'UPLOAD': if (!privileged) { return deny(); } - return void upload(blobStagingPath, Sessions, safeKey, msg[1], function (e, len) { + return void upload(paths, Sessions, safeKey, msg[1], function (e, len) { Respond(e, len); }); case 'UPLOAD_STATUS': if (!privileged) { return deny(); } - return void upload_status(blobStagingPath, Sessions, safeKey, function (e, stat) { + return void upload_status(paths, Sessions, safeKey, function (e, stat) { Respond(e, stat); }); case 'UPLOAD_COMPLETE': if (!privileged) { return deny(); } - return void upload_complete(blobStagingPath, blobPath, Sessions, safeKey, function (e, hash) { + return void upload_complete(paths, Sessions, safeKey, function (e, hash) { Respond(e, hash); }); case 'UPLOAD_CANCEL': if (!privileged) { return deny(); } - return void upload_cancel(blobStagingPath, Sessions, safeKey, function (e) { + return void upload_cancel(paths, Sessions, safeKey, function (e) { Respond(e); }); default: From 5aba2d4640c5ac7cc82835c17b59d220532e0fe3 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 10 May 2017 15:36:34 +0200 Subject: [PATCH 5/6] remove unused variable --- www/assert/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/assert/main.js b/www/assert/main.js index 449834a61..7da11f408 100644 --- a/www/assert/main.js +++ b/www/assert/main.js @@ -19,7 +19,7 @@ define([ var count = ASSERTS.length; var successes = 0; - var done = function (err, index) { + var done = function (err) { count--; if (err) { failMessages.push(err); } else { successes++; } From 1525712deb960bc8f1acbcda548d4f3b2fb850a2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 10 May 2017 17:13:14 +0200 Subject: [PATCH 6/6] new encrypted file format --- www/file/file-crypto.js | 96 ++++++++++++++++------------------------- www/file/main.js | 84 ++++++++++++++++-------------------- www/file/test/main.js | 62 +++++++++++--------------- 3 files changed, 100 insertions(+), 142 deletions(-) diff --git a/www/file/file-crypto.js b/www/file/file-crypto.js index 35e2f8037..5290dfb80 100644 --- a/www/file/file-crypto.js +++ b/www/file/file-crypto.js @@ -57,55 +57,30 @@ define([ }, [])); }; - var padChunk = function (A) { - var padding; - if (A.length === plainChunkLength) { return A; } - if (A.length < plainChunkLength) { - padding = new Array(plainChunkLength - A.length).fill(32); - return A.concat(padding); - } - if (A.length > plainChunkLength) { - // how many times larger is it? - var chunks = Math.ceil(A.length / plainChunkLength); - padding = new Array((plainChunkLength * chunks) - A.length).fill(32); - return A.concat(padding); - } - }; - var decrypt = function (u8, key, cb) { + var fail = function (e) { + cb(e || "DECRYPTION_ERROR"); + }; + var nonce = createNonce(); var i = 0; - decodePrefix([]); // TODO - var takeChunk = function () { - var start = i * cypherChunkLength; - var end = start + cypherChunkLength; - i++; - var box = new Uint8Array(u8.subarray(start, end)); - - // decrypt the chunk - var plaintext = Nacl.secretbox.open(box, nonce, key); - increment(nonce); - return plaintext; - }; - - var buffer = ''; + var prefix = u8.subarray(0, 2); + var metadataLength = decodePrefix(prefix); var res = { metadata: undefined, }; - // decrypt metadata - var chunk; - for (; !res.metadata && i * cypherChunkLength < u8.length;) { - chunk = takeChunk(); - buffer += Nacl.util.encodeUTF8(chunk); - try { - res.metadata = JSON.parse(buffer); - //console.log(res.metadata); - } catch (e) { - console.log('buffering another chunk for metadata'); - } + var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength)); + + var metaChunk = Nacl.secretbox.open(metaBox, nonce, key); + increment(nonce); + + try { + res.metadata = JSON.parse(Nacl.util.encodeUTF8(metaChunk)); + } catch (e) { + return fail('E_METADATA_DECRYPTION'); } if (!res.metadata) { @@ -114,12 +89,21 @@ define([ }); } - var fail = function () { - cb("DECRYPTION_ERROR"); + var takeChunk = function () { + var start = i * cypherChunkLength + 2 + metadataLength; + var end = start + cypherChunkLength; + i++; + var box = new Uint8Array(u8.subarray(start, end)); + + // decrypt the chunk + var plaintext = Nacl.secretbox.open(box, nonce, key); + increment(nonce); + return plaintext; }; var chunks = []; // decrypt file contents + var chunk; for (;i * cypherChunkLength < u8.length;) { chunk = takeChunk(); if (!chunk) { @@ -139,15 +123,12 @@ define([ var encrypt = function (u8, metadata, key) { var nonce = createNonce(); - encodePrefix(); // TODO - // encode metadata var metaBuffer = Array.prototype.slice .call(Nacl.util.decodeUTF8(JSON.stringify(metadata))); - var plaintext = new Uint8Array(padChunk(metaBuffer)); + var plaintext = new Uint8Array(metaBuffer); - var j = 0; var i = 0; /* @@ -164,22 +145,21 @@ define([ var part; var box; - if (state === 0) { // metadata... - start = j * plainChunkLength; - end = start + plainChunkLength; + // DONE + if (state === 2) { return void cb(); } - part = plaintext.subarray(start, end); + if (state === 0) { // metadata... + part = new Uint8Array(plaintext); box = Nacl.secretbox(part, nonce, key); increment(nonce); - j++; - - // metadata is done - if (j * plainChunkLength >= plaintext.length) { - return void cb(state++, box); + if (box.length > 65535) { + return void cb('METADATA_TOO_LARGE'); } - - return void cb(state, box); + var prefixed = new Uint8Array(encodePrefix(box.length) + .concat(slice(box))); + state++; + return void cb(void 0, prefixed); } // encrypt the rest of the file... @@ -194,7 +174,7 @@ define([ // regular data is done if (i * plainChunkLength >= u8.length) { state = 2; } - return void cb(state, box); + return void cb(void 0, box); }; return next; diff --git a/www/file/main.js b/www/file/main.js index 66e0f6c25..32bcaafe6 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -47,55 +47,43 @@ define([ }); }; - var again = function (state, box) { - switch (state) { - case 0: - sendChunk(box, function (e) { - if (e) { return console.error(e); } - next(again); - }); - break; - case 1: - sendChunk(box, function (e) { - if (e) { return console.error(e); } - next(again); - }); - break; - case 2: - sendChunk(box, function (e) { - if (e) { return console.error(e); } - Cryptpad.rpc.send('UPLOAD_COMPLETE', '', function (e, res) { - if (e) { return void console.error(e); } - var id = res[0]; - 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); - window.location.hash = Cryptpad.getFileHashFromKeys(id, b64Key); - - $form.hide(); - - APP.toolbar.addElement(['fileshare'], {}); - - // check if the uploaded file can be decrypted - var newU8 = FileCrypto.joinChunks(chunks); - FileCrypto.decrypt(newU8, key, function (e, res) { - if (e) { return console.error(e); } - var title = document.title = res.metadata.name; - myFile = res.content; - myDataType = res.metadata.type; - - var defaultName = Cryptpad.getDefaultName(Cryptpad.parsePadUrl(window.location.href)); - Title.updateTitle(title || defaultName); - APP.toolbar.title.show(); - Cryptpad.alert("successfully uploaded: " + title); - }); - }); - }); - break; - default: - throw new Error("E_INVAL_STATE"); + var again = function (err, box) { + if (err) { throw new Error(err); } + if (box) { + return void sendChunk(box, function (e) { + if (e) { return console.error(e); } + next(again); + }); } + + // if not box then done + Cryptpad.rpc.send('UPLOAD_COMPLETE', '', function (e, res) { + if (e) { return void console.error(e); } + var id = res[0]; + 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); + window.location.hash = Cryptpad.getFileHashFromKeys(id, b64Key); + + $form.hide(); + + APP.toolbar.addElement(['fileshare'], {}); + + // check if the uploaded file can be decrypted + var newU8 = FileCrypto.joinChunks(chunks); + FileCrypto.decrypt(newU8, key, function (e, res) { + if (e) { return console.error(e); } + var title = document.title = res.metadata.name; + myFile = res.content; + myDataType = res.metadata.type; + + var defaultName = Cryptpad.getDefaultName(Cryptpad.parsePadUrl(window.location.href)); + Title.updateTitle(title || defaultName); + APP.toolbar.title.show(); + Cryptpad.alert("successfully uploaded: " + title); + }); + }); }; Cryptpad.rpc.send('UPLOAD_STATUS', '', function (e, pending) { diff --git a/www/file/test/main.js b/www/file/test/main.js index c2b24e4f2..5ec71126d 100644 --- a/www/file/test/main.js +++ b/www/file/test/main.js @@ -11,7 +11,6 @@ define([ '/bower_components/file-saver/FileSaver.min.js', ], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) { var Nacl = window.nacl; - $(function () { var filesAreSame = function (a, b) { @@ -31,8 +30,8 @@ define([ var upload = function (blob, metadata) { var u8 = new Uint8Array(blob); - var key = Nacl.randomBytes(32); + var next = FileCrypto.encrypt(u8, metadata, key); var chunks = []; @@ -41,41 +40,32 @@ define([ cb(); }; - var again = function (state, box) { - switch (state) { - case 0: - sendChunk(box, function (e) { - if (e) { return console.error(e); } - next(again); - }); - break; - case 1: - sendChunk(box, function (e) { - if (e) { return console.error(e); } - next(again); - }); - break; - case 2: - sendChunk(box, function (e) { - if (e) { return console.error(e); } - - // check if the uploaded file can be decrypted - var newU8 = FileCrypto.joinChunks(chunks); - FileCrypto.decrypt(newU8, key, function (e, res) { - if (e) { return Cryptpad.alert(e); } + var again = function (err, box) { + if (err) { throw new Error(err); } - if (filesAreSame(blob, res.content) && - metadataIsSame(res.metadata, metadata)) { - Cryptpad.alert("successfully uploaded"); - } else { - Cryptpad.alert('encryption failure!'); - } - }); - }); - break; - default: - throw new Error("E_INVAL_STATE"); + if (box) { + return void sendChunk(box, function (e) { + if (e) { + console.error(e); + return Cryptpad.alert('Something went wrong'); + } + next(again); + }); } + // check if the uploaded file can be decrypted + var newU8 = FileCrypto.joinChunks(chunks); + + console.log('encrypted file with metadata is %s uint8s', newU8.length); + FileCrypto.decrypt(newU8, key, function (e, res) { + if (e) { return Cryptpad.alert(e); } + + if (filesAreSame(blob, res.content) && + metadataIsSame(res.metadata, metadata)) { + Cryptpad.alert("successfully uploaded"); + } else { + Cryptpad.alert('encryption failure!'); + } + }); }; next(again); }; @@ -83,7 +73,7 @@ define([ var andThen = function () { var src = '/customize/cryptofist_mini.png'; Cryptpad.fetch(src, function (e, file) { - console.log(file); + console.log('original file is %s uint8s', file.length); upload(file, { pew: 'pew', bang: 'bang',