diff --git a/www/file/file-crypto.js b/www/file/file-crypto.js new file mode 100644 index 000000000..8c520dd59 --- /dev/null +++ b/www/file/file-crypto.js @@ -0,0 +1,88 @@ +define([ + '/bower_components/tweetnacl/nacl-fast.min.js', +], function () { + var Nacl = window.nacl; + + var chunkLength = 131088; + + var slice = function (A) { + return Array.prototype.slice.call(A); + }; + + var increment = function (N) { + var l = N.length; + while (l-- > 1) { + if (N[l] !== 255) { return void N[l]++; } + N[l] = 0; + if (l === 0) { return true; } + } + }; + + var joinChunks = function (B) { + return new Uint8Array(chunks.reduce(function (A, B) { + return slice(A).concat(slice(B)); + }, [])); + }; + + var decrypt = function (u8, key, cb) { + var nonce = new Uint8Array(new Array(24).fill(0)); + var i = 0; + var takeChunk = function () { + let start = i * chunkLength; + let end = start + chunkLength; + i++; + let box = new Uint8Array(u8.subarray(start, end)); + + // decrypt the chunk + let plaintext = Nacl.secretbox.open(box, nonce, key); + increment(nonce); + return plaintext; + }; + + var buffer = ''; + + var res = { + metadata: undefined, + }; + + // decrypt metadata + for (; !res.metadata && i * chunkLength < u8.length;) { + var 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'); + } + } + + if (!res.metadata) { + return void setTimeout(function () { + cb('NO_METADATA'); + }); + } + + var chunks = []; + // decrypt file contents + for (;i * chunkLength < u8.length;) { + let chunk = takeChunk(); + if (!chunk) { + return void window.setTimeout(function () { + cb('DECRYPTION_ERROR'); + }); + //throw new Error('failed to parse'); + } + chunks.push(chunk); + } + + // send chunks + res.content = joinChunks(chunks); + + cb(void 0, res); + }; + + return { + decrypt: decrypt, + }; +});