diff --git a/.jshintignore b/.jshintignore index 51636ed77..93e467aef 100644 --- a/.jshintignore +++ b/.jshintignore @@ -10,3 +10,4 @@ NetFluxWebsocketSrv.js NetFluxWebsocketServer.js WebRTCSrv.js www/common/media-tag.js +www/scratch diff --git a/www/file/file-crypto.js b/www/file/file-crypto.js index 8c520dd59..924cbe334 100644 --- a/www/file/file-crypto.js +++ b/www/file/file-crypto.js @@ -2,39 +2,75 @@ define([ '/bower_components/tweetnacl/nacl-fast.min.js', ], function () { var Nacl = window.nacl; + var PARANOIA = true; - var chunkLength = 131088; + var plainChunkLength = 128 * 1024; + var cypherChunkLength = 131088; var slice = function (A) { return Array.prototype.slice.call(A); }; + var createNonce = function () { + return new Uint8Array(new Array(24).fill(0)); + }; + var increment = function (N) { var l = N.length; while (l-- > 1) { - if (N[l] !== 255) { return void N[l]++; } + if (PARANOIA) { + if (typeof(N[l]) !== 'number') { + throw new Error('E_UNSAFE_TYPE'); + } + if (N[l] > 255) { + throw new Error('E_OUT_OF_BOUNDS'); + } + } + /* jshint probably suspects this is unsafe because we lack types + but as long as this is only used on nonces, it should be safe */ + if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line N[l] = 0; + + // you don't need to worry about this running out. + // you'd need a REAAAALLY big file if (l === 0) { return true; } } }; - var joinChunks = function (B) { + var joinChunks = function (chunks) { return new Uint8Array(chunks.reduce(function (A, B) { return slice(A).concat(slice(B)); }, [])); }; + 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 nonce = new Uint8Array(new Array(24).fill(0)); + var nonce = createNonce(); var i = 0; + var takeChunk = function () { - let start = i * chunkLength; - let end = start + chunkLength; + var start = i * cypherChunkLength; + var end = start + cypherChunkLength; i++; - let box = new Uint8Array(u8.subarray(start, end)); + var box = new Uint8Array(u8.subarray(start, end)); // decrypt the chunk - let plaintext = Nacl.secretbox.open(box, nonce, key); + var plaintext = Nacl.secretbox.open(box, nonce, key); + // TODO handle nonce-too-large-error increment(nonce); return plaintext; }; @@ -46,8 +82,9 @@ define([ }; // decrypt metadata - for (; !res.metadata && i * chunkLength < u8.length;) { - var chunk = takeChunk(); + var chunk; + for (; !res.metadata && i * cypherChunkLength < u8.length;) { + chunk = takeChunk(); buffer += Nacl.util.encodeUTF8(chunk); try { res.metadata = JSON.parse(buffer); @@ -63,15 +100,16 @@ define([ }); } + var fail = function () { + cb("DECRYPTION_ERROR"); + }; + var chunks = []; // decrypt file contents - for (;i * chunkLength < u8.length;) { - let chunk = takeChunk(); + for (;i * cypherChunkLength < u8.length;) { + chunk = takeChunk(); if (!chunk) { - return void window.setTimeout(function () { - cb('DECRYPTION_ERROR'); - }); - //throw new Error('failed to parse'); + return window.setTimeout(fail); } chunks.push(chunk); } @@ -82,7 +120,63 @@ define([ cb(void 0, res); }; + // metadata + /* { filename: 'raccoon.jpg', type: 'image/jpeg' } */ + + + /* TODO + in your callback, return an object which you can iterate... + + + */ + + var encrypt = function (u8, metadata, key, cb) { + var nonce = createNonce(); + + // encode metadata + var metaBuffer = Array.prototype.slice + .call(Nacl.util.decodeUTF8(JSON.stringify(metadata))); + + var plaintext = new Uint8Array(padChunk(metaBuffer)); + + var chunks = []; + var j = 0; + + var start; + var end; + + var part; + var box; + + // prepend some metadata + for (;j * plainChunkLength < plaintext.length; j++) { + start = j * plainChunkLength; + end = start + plainChunkLength; + + part = plaintext.subarray(start, end); + box = Nacl.secretbox(part, nonce, key); + chunks.push(box); + increment(nonce); + } + + // append the encrypted file chunks + var i = 0; + for (;i * plainChunkLength < u8.length; i++) { + start = i * plainChunkLength; + end = start + plainChunkLength; + + part = new Uint8Array(u8.subarray(start, end)); + box = Nacl.secretbox(part, nonce, key); + chunks.push(box); + increment(nonce); + } + + + // TODO do something with the chunks... + }; + return { decrypt: decrypt, + encrypt: encrypt, }; }); diff --git a/www/file/inner.html b/www/file/inner.html index 7f315cef2..09f627842 100644 --- a/www/file/inner.html +++ b/www/file/inner.html @@ -14,14 +14,44 @@ padding: 0px; display: inline-block; } - media-tag * { - max-width: 100%; + #file { + display: block; + height: 300px; + width: 300px; + border: 2px solid black; + margin: 50px; } + + .inputfile { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; + } + .inputfile + label { + border: 2px solid black; + display: block; + height: 500px; + width: 500px; + background-color: rgba(50, 50, 50, .10); + margin: 50px; + } + + .inputfile:focus + label, + .inputfile + label:hover { + background-color: rgba(50, 50, 50, 0.30); + } +
-