Explorar o código

Integrated Comlink.

Pushkar Anand %!s(int64=4) %!d(string=hai) anos
pai
achega
760a085dfd

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
   "dependencies": {
     "axios": "^0.20.0",
     "bootstrap": "^4.5.2",
+    "comlink": "^4.3.0",
     "formik": "^2.1.5",
     "http-proxy-middleware": "^1.0.5",
     "libsodium-wrappers": "^0.7.8",

+ 8 - 16
src/services/fileService.ts

@@ -1,8 +1,11 @@
-import aescrypt from "utils/aescrypt";
 import { getEndpoint } from "utils/common/apiUtil";
-import { decrypt } from "utils/crypto/aes";
-import { strToUint8 } from "utils/crypto/common";
 import HTTPService from "./HTTPService";
+import * as Comlink from "comlink";
+
+const decryptMetadata: any = typeof window !== 'undefined'
+    && Comlink.wrap(new Worker("worker/decryptMetadata.worker.js", { type: 'module' }));
+const decryptThumbnail: any = typeof window !== 'undefined'
+    && Comlink.wrap(new Worker("worker/decryptThumbnail.worker.js", { type: 'module' }));
 
 const ENDPOINT = getEndpoint();
 
@@ -12,7 +15,6 @@ export interface decryptionParams {
     header: string;
     nonce: string;
 };
-
 export interface fileData {
     id: number;
     file: {
@@ -36,21 +38,11 @@ export interface fileData {
 };
 
 const getFileMetaDataUsingWorker = (data: any, key: string) => {
-    return new Promise((resolve) => {
-        const worker = new Worker('worker/decryptMetadata.worker.js', { type: 'module' });
-        const onWorkerMessage = (event) => resolve(event.data);
-        worker.addEventListener('message', onWorkerMessage);
-        worker.postMessage({ data, key });
-    });
+    return decryptMetadata({ data, key });
 }
 
 const getFileUsingWorker = (data: any, key: string) => {
-    return new Promise((resolve) => {
-        const worker = new Worker('worker/decryptThumbnail.worker.js', { type: 'module' });
-        const onWorkerMessage = (event) => resolve(event.data);
-        worker.addEventListener('message', onWorkerMessage);
-        worker.postMessage({ data, key });
-    });
+    return decryptThumbnail({ data, key });
 }
 
 export const getFiles = async (sinceTime: string, token: string, limit: string, key: string) => {

+ 0 - 1697
src/utils/aescrypt/aescrypt.js

@@ -1,1697 +0,0 @@
-
-
-/*--------------------------------------------------------------------------------------------
-
-    MIT license.
-
-    Copyright 2017 Aaron Flin
-
-    Permission is hereby granted, free of charge, to any person obtaining a copy
-    of this software and associated documentation files (the "Software"), to
-    deal in the Software without restriction, including without limitation the
-    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-    sell copies of the Software, and to permit persons to whom the Software is
-    furnished to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be included in
-    all copies or substantial portions of the Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-    IN THE SOFTWARE.
-
-
-----------------------------------------------------------------------------------------------*/
-import forge from 'node-forge';
-
-//start collecting random data for forge RNG
-var d = new Date();
-
-if (typeof document !== 'undefined') {
-    forge.random.collect(d.getTime(), 32);
-    //in normal version, we can get the mouse movements and add that as random data.
-    document.onmousemove = function (e) {
-        forge.random.collectInt(e.clientX, 16);
-        forge.random.collectInt(e.clientY, 16);
-    };
-}
-
-
-
-export var aescrypt = (function () {
-
-    var b2h = forge.util.bytesToHex;
-
-    function a2h(a) {
-        return forge.util.bytesToHex(arrayToString(a));
-    }
-
-    var toType = function (obj) {
-        return ({}).toString.call(obj).match(/\s([a-zA-Z0-9]+)/)[1].toLowerCase();
-    }
-
-    /* copy string or uint8array into a uint8array 
-        start is where in output array to start copying
-        max is max bytes to copy from input string/array
-    	
-        does not check if a is big enough to take s
-    */
-
-    function copyToArray(s, a, start, max) {
-        if (max === undefined) max = s.length;
-        if (start === undefined) start = 0;
-        var t = toType(s);
-
-        if (t == "arraybuffer") {
-            s = new Uint8Array(s);
-            t = 'uint8array';
-        }
-
-        switch (t) {
-            case 'array':
-            case 'uint8array':
-                //for (var i=0; i<max;i++)
-                //      a[i+start]=s[i];
-                // not much of an improvement using .set(), if any at all
-                if (max >= s.length) {
-                    a.set(s, start);
-                    return s.length;
-                } else if (t == 'array') {
-                    a.set(s.slice(0, max), start);
-                } else {
-                    a.set(s.subarray(0, max), start);
-                }
-                return max;
-            case 'string':
-                for (var i = 0; i < max; i++)
-                    a[i + start] = s.charCodeAt(i);
-                return i;
-            default:
-                return 0;
-        }
-    }
-
-
-    /* convert a uint8array or arraybuffer to a string */
-    function arrayToString(array) {
-        var ret = "";
-        if (toType(array) == 'arraybuffer')
-            array = new Uint8Array(array);
-        return String.fromCharCode.apply(null, array);
-    }
-
-    function toU8(data, nostrings) {
-        switch (toType(data)) {
-            case 'uint8array':
-                return data;
-            case 'arraybuffer':
-                return (new Uint8Array(data));
-            case 'string':
-                if (nostrings) return;
-            //no break
-            case 'array':
-                // is this the best way to do this, given encfile could be large?
-                var x = new Uint8Array(data.length);
-                copyToArray(data, x, 0);
-                return x;
-            default:
-                break;
-        }
-    }
-
-    function isblank(a, start, len) {
-        var end = start + len;
-        for (var i = start; i < end; i++)
-            if (a[i] != 0) return false;
-        return true;
-    }
-
-    function to16(str) {
-        var out, i, len, c, c2;
-        var char2, char3;
-
-        out = "";
-        len = str.length;
-        i = 0;
-        while (i < len) {
-            c = str.charCodeAt(i++);
-            c2 = c >>> 8;
-            c = c & 0xFF;
-            out += String.fromCharCode(c);
-            out += String.fromCharCode(c2);
-        }
-        return out;
-    }
-
-    function ivpasstokey(iv, pass) {
-        var hashbuf = new Uint8Array(32);
-        var hashstr, hashstr2;
-        var p = to16(pass);
-        copyToArray(iv, hashbuf, 0);
-        hashstr = arrayToString(hashbuf);
-        hashstr2 = hashstr;
-        // do this outside the loop, its expensive, and many times more expensive in chrome than firefox
-        var md = forge.md.sha256.create();
-        for (var i = 0; i < 8192; i++) {
-            //start() is not expensive, but allows us to start over with same object
-            md.start();
-            md.update(hashstr + p);
-            hashstr = md.digest().data;
-        }
-        return hashstr;
-    }
-
-    function xor16(a, b) {
-        var x = new Uint8Array(16);
-        for (i = 0; i < 16; i++)
-            x[i] = a.charCodeAt(i) ^ b.charCodeAt(i);
-        return arrayToString(x);
-    }
-
-    function makeext(x) {
-        var lenbytes = new String;
-        var t = new String;
-        var len = 0;
-        if (typeof x == 'string') {
-            len = x.length + 1;
-            t = x + String.fromCharCode(0);
-        } else {
-            //assume an array of strings
-            for (var i = 0; i < x.length; i++) {
-                len += x[i].length + 1;
-                t += x[i] + String.fromCharCode(0);
-            }
-        }
-
-        return String.fromCharCode(len >> 8) + String.fromCharCode(len & 0xFF) + t;
-    }
-
-    function cipherblock(key, iv, block) {
-        var cipher = forge.cipher.createCipher('AES-CBC', key);
-        cipher.start({ iv: iv });
-        cipher.update(forge.util.createBuffer(block));
-        cipher.finish();
-        var mod = block.length % 16;
-        //console.log(mod);
-        if (mod != 0)
-            return cipher.output.data;
-        // don't pad file that otherwise fits exactly into 16 byte blocks.
-        // forge needs the extra room for encoding a padding number in the plaintext
-        // aescrypt format has the padding number outside the encrypted text.
-        else
-            return cipher.output.data.slice(0, -16);
-
-    }
-
-    /* decipher some crypttext.  skipend fixes the truncation problem that prompted much of the commented out code below */
-
-    function decipherblock(key, iv, block, skipend) {
-        var decipher = forge.cipher.createDecipher('AES-CBC', key);
-        decipher.start({ iv: iv });
-        decipher.update(forge.util.createBuffer(block));
-        //too many negatives for a normal human brain (well, at least mine)
-        // skip end of file padding if skipend = true;
-        if (skipend !== true) decipher.finish();
-        return decipher.output.data;
-    }
-
-    /* converting a large file to a string chokes, we need to do this a bit at a time */
-    /* if its big, pass a Uint8Array instead of string                                */
-    var chunksize = 16 * 1024;
-    function hmacblock(key, block) {
-        var hmac = forge.hmac.create();
-        hmac.start('sha256', key);
-        if (toType(block) == 'uint8array') {
-            var l = block.length;
-            //console.log(l);
-            for (var i = 0; i < l; i += chunksize) {
-                var end = ((i + chunksize) < l) ? i + chunksize : l;
-                var s = arrayToString(block.subarray(i, end));
-                //console.log("updating with:"+s);
-                //console.log(s.length);
-                hmac.update(s);
-            }
-        } else {
-            hmac.update(block);
-        }
-        return hmac.digest().data;
-    }
-
-    //same thing, but use existing hmac, and don't finish
-    function hmacchunkblock(hmac, block) {
-        if (toType(block) == 'uint8array') {
-            var l = block.length, s;
-            //console.log(l);
-            for (var i = 0; i < l; i += chunksize) {
-                //var end = ( (i+chunksize)< l) ? i+chunksize: l;
-                //var s=arrayToString(block.subarray(i,end));
-                //console.log("updating with:"+s);
-                //console.log(s.length);
-                s = arrayToString(block.subarray(i, i + chunksize));
-                hmac.update(s);
-            }
-        } else {
-            hmac.update(block);
-        }
-    }
-
-
-    /* get a key and fileiv from a 96 byte credential block */
-    /* cb is an Uint8Array containing the credential block and pass is used to decrypt it */
-
-    function getkey(cb, pass) {
-        var keyiv, bufi = 0, enckeyblk, hdat, hashkey, hdatcomp, keyblock, fileiv, key, i;
-        keyiv = arrayToString(cb.subarray(bufi, bufi + 16));
-        bufi += 16;
-        enckeyblk = arrayToString(cb.subarray(bufi, bufi + 48));
-        bufi += 48;
-        hdat = arrayToString(cb.subarray(bufi, bufi + 32));
-        bufi += 32;
-        hashkey = ivpasstokey(keyiv, pass);
-        hdatcomp = hmacblock(hashkey, enckeyblk);
-        //console.log ("keyiv=" + forge.util.bytesToHex(keyiv));console.log ("enckeyblk=" + forge.util.bytesToHex(enckeyblk));console.log ("hdat=" + forge.util.bytesToHex(hdat));console.log ("hashkey=" + forge.util.bytesToHex(hashkey));console.log("hdatcomp=" + forge.util.bytesToHex(hdatcomp));
-        if (hdat != hdatcomp) {
-            //return error
-            //console.log("hmac does not match. Bad password or file corruption");
-            return { key: "", fileiv: "", error: "hmac does not match. Bad password or file corruption" };
-        }
-        i = 0;
-        keyblock = decipherblock(hashkey, keyiv, enckeyblk, true);
-        if (keyblock.length != 48) {
-            console.log("This shouldn't happen anymore. Please report this.");
-        }
-        fileiv = keyblock.slice(0, 16);
-        key = keyblock.slice(16, 48);
-        //console.log("keyblock.length="+keyblock.length);console.log ("key=" + forge.util.bytesToHex(key));console.log ("fileiv=" + forge.util.bytesToHex(fileiv));
-        return { key: key, fileiv: fileiv, error: "" };
-    }
-
-
-    /*  make the 96 byte credential block ( keyiv, encrypted fileiv+key, hmac (fileiv+key) )   */
-    /*  pass required, will generate key and fileiv if not provided                        */
-    function newcredentialblock(pass, key, fileiv) {
-        var keyiv, hashkey, keyblock, enckeyblk, tblock;
-        var buffer = new Uint8Array(96);
-        var d = new Date();
-        forge.random.collectInt(d.getTime(), 32);
-
-        //encrypt the iv-key combination (those used to encrypt file data)
-        // and chop off the padding
-
-        keyiv = forge.random.getBytesSync(16);
-        hashkey = ivpasstokey(keyiv, pass);
-        if (fileiv === undefined) fileiv = forge.random.getBytesSync(16);
-        if (key === undefined) key = forge.random.getBytesSync(32);
-        keyblock = fileiv + key;
-
-        enckeyblk = cipherblock(hashkey, keyiv, keyblock).slice(0, 48);
-
-        //this appears to be no longer a problem when using decipherblock(,,,true)
-        //i.e. dont finish(), and skip padding removal which we don't have
-        tblock = decipherblock(hashkey, keyiv, enckeyblk, true);
-        if (tblock != keyblock) {
-            //console.log("failed to find keys and ivs that forge liked...");
-            //console.log("keyblock=" + forge.util.bytesToHex(keyblock));
-            return "";
-        }
-
-        //console.log("keyiv="+forge.util.bytesToHex(keyiv));console.log("hashkey=" + forge.util.bytesToHex(hashkey));console.log("keyblock=" + forge.util.bytesToHex(keyblock));console.log("fileiv=" + forge.util.bytesToHex(fileiv));console.log("key=" + forge.util.bytesToHex(key));console.log("aes-cbc enckeyblock  ="+forge.util.bytesToHex(enckeyblk));console.log("encrypted iv+key length=" + enckeyblk.length);
-
-        // 16 + 48 + 32 bytes = 96 for entire record.
-        copyToArray((keyiv + enckeyblk + hmacblock(hashkey, enckeyblk)), buffer, 0)
-        return { buffer: buffer, key: key, fileiv: fileiv };
-    }
-
-
-    /*  get the key from main keyblock or in credential blocks      *
-     *  takes a Unit8Array                                          *
-     *  if inextcbonly==true, we will only look for the pass in the    *
-     *  extended cb block                                           *
-     *  if offset is set, we start looking for extensions at that   *
-     *  location instead of 5 bytes in                              */
-
-    var getanykey = function (file, pass, inextcbonly, offset, deletekey) {
-        var file, bufi, error, credext, credsize, newblock,
-            key, encdata, data, i = 0, cb;
-
-        //console.log("file="+ forge.util.bytesToHex(arrayToString(file)));
-        //find our extension, skip past rest. Assume from start of file, but take offset if provided
-        bufi = (offset === undefined) ? 5 : offset;
-
-        while (bufi < file.length && (file[bufi] != 0 || file[bufi + 1] != 0)) {
-            var sig, size = (file[bufi] << 8) + file[bufi + 1];
-            if (size > 17) {
-                sig = arrayToString(file.subarray(bufi + 2, bufi + 17));
-                if (sig == "enckeyblk v 0.1") {
-                    credext = bufi + 18; //2 size bytes plus ("enckeyblk v 0.1").length + 1 (0x00 at end)
-                    credsize = size - 16;
-                    //no need to go further, and we might not have a 0x0000 at the end anyway
-                    if (inextcbonly) break;
-                }
-            }
-            bufi += size + 2;
-            if (bufi > file.length) {
-                //return error
-                //console.log("error finding end of extensions");
-                return { key: "", fileiv: "", error: "error finding end of extensions" };
-            }
-        }
-        // if true skip and only look in extended credential block for key.  Skip checking of main block for this password
-        if (inextcbonly !== true) {
-            bufi += 2;
-            //get keyiv, encrypted key and file iv and hmac of encrypted key and file iv (credential block)
-            cb = file.subarray(bufi, bufi + 96);
-            bufi += 96;
-            //console.log("looking in main block");
-            key = getkey(cb, pass);
-            if (key.error == "")
-                return { key: key.key, fileiv: key.fileiv, error: "", masterkey: true };
-
-        }
-        var isarray = false;
-        var indexarray;
-        if (credext !== undefined) {
-            // find non empty blocks and check for key
-            // also check for request to delete key
-            // if deletekey is an array, it should be an array of keyslot positions to delete
-            // otherwise if ===true, it should be deleted if pass matches
-            var j = 0;
-            if (deletekey && typeof deletekey == 'array') {
-                isarray = true;
-                indexarray = [];
-            }
-            for (var i = 0; i < credsize; i += 96) {
-                var start = i + credext;
-                if (!isblank(file, start, 96)) {
-                    cb = file.subarray(start, start + 96);
-                    //console.log("cb="+forge.util.bytesToHex(arrayToString(cb)));
-                    // if we are deleting keys at certain positions
-                    if (isarray) {
-                        for (var k = 0; k < deletekey.length; k++) {
-                            if (deletekey[k] == j) {
-                                copyToArray(new Uint8Array(96), file, start);
-                                indexarray.push(j);
-                                break;
-                            }
-                        }
-                        //dont check for key.  if using array and inextcbonly===true, password can be blank
-                        continue;
-                    }
-                    //console.log("checking keyslot "+j);
-                    key = getkey(cb, pass);
-                    if (key.error == "") {
-                        if (deletekey === true) {
-                            copyToArray(new Uint8Array(96), file, start);
-                        }
-                        return { key: key.key, fileiv: key.fileiv, error: "", index: j, masterkey: false };
-                    }
-                }
-                //else console.log("empty keyslot: "+j);
-                j++;
-            }
-        }
-        if (isarray)
-            return { key: "", fileiv: '', error: '', index: indexarray };
-        else
-            return { key: "", fileiv: '', error: "Key not found using this password" };
-    }
-
-    // an array of uint8arrays, with functions to add more and extract bytes
-    // argument file and argument input in put(input) must be uint8arrays
-    // no checking or error messages.
-    function newbytebuf(file) {
-        var buffer = [];
-
-        if (file !== undefined) {
-            file = toU8(file);
-
-            if (toType(file) == 'uint8array') buffer = [file];
-
-        }
-
-        function getarrayslength(arrays) {
-            var len = 0;
-            for (var i = 0; i < arrays.length; i++)
-                len += arrays[i].length;
-            return len;
-        }
-
-        function joinarray(arrays) {
-            var len = getarrayslength(arrays), ret = new Uint8Array(len);
-
-            if (len == 0) return ret;
-
-            len = 0;
-            for (var i = 0; i < arrays.length; i++) {
-                ret.set(arrays[i], len);
-                len += arrays[i].length;
-            }
-
-            return ret;
-        }
-
-
-        return {
-            // read from this buffer (amount gotten with .get() )
-            read: 0,
-            // written to this buffer (amount pushed with put() )
-            written: getarrayslength(buffer),
-            eof: false,
-            buffers: buffer,
-            length: 0,
-            done: function () { this.eof = true; return this; },
-            // only use uint8arrays for input
-            put: function (input) {
-                if (this.eof) {
-                    //don't accept more data after eof==true
-                    //console.log("already done, can't take more data");
-                    this.error = "already done, can't take more data";
-                    return this;
-                }
-
-                input = toU8(input);
-
-                if (input !== undefined) {
-                    this.written += input.length;
-                    this.length = this.written - this.read;
-                    this.buffers.push(input);
-                }
-                else {
-                    this.error = "invalid input data in put()";
-                    //console.log("invalid input data in put()");
-                }
-                return this;
-            },
-            getlength: function () {
-                this.length = getarrayslength(this.buffers);
-                return this.length;
-            },
-            // optimized if data is still in underlying arraybuffer, but we won't go crazy.  If
-            // the unget data covers more than one buffer, then probably not worth it.
-            // if data is an int, we are putting back data that we just got, in order with no gaps.
-            // However, if that isn't possible, x should be the actual data so we have a fallback.
-            // if data is an uint8array, we just unshift the new data
-            unget: function (data, x) {
-                if ((typeof data == 'string' || typeof data == 'number') &&
-                    Math.round(data) == data) {
-                    if (data <= this.slice && this.buffers.length) {
-                        this.slice -= data.length;
-                        //reslice the first buffer
-                        var wholebuf = new Uint8Array(this.buffers[0].buffer);
-                        this.buffers[0] = wholebuf.subarray(this.slice);
-                        this.read -= data;
-                        this.length = this.written - this.read;
-                        return this;
-                    } else {
-                        //overlap, we'll unshift the data instead
-                        data = x;
-                    }
-                }
-                data = toU8(data);
-                this.read -= data.length;
-                this.buffers.unshift(data);
-                this.length = this.written - this.read;
-                return this;
-            },
-            // size=-1:  get all data as one array
-            // size>0:   shift size data from buffer.  if buffer smaller than size, return undefined
-            // size==undefined:  get a convenient amount of data out of the buffer;  if no data left, return undefined
-            get: function (size) {
-                var filebuf = this.buffers;
-                if (!filebuf || !filebuf.length) return;
-                // get the first block of data if undefined, or if that was the size requested
-                if (size === undefined || size == filebuf[0].length) {
-                    this.slice = 0;
-                    this.read += size;
-                    this.length = this.written - this.read;
-                    return (filebuf.shift());
-                }
-                //return everything if -1
-                //if no data in buffer, return undefined;
-                if (size == -1 || size == 'all') {
-                    var ret = joinarray(this.buffers);
-                    this.buffers = [];
-                    this.slice = 0;
-                    this.read += ret.length;
-                    this.length = this.written - this.read;
-                    return ret;
-                }
-
-                var endbuf = 0, tail;
-                var i = 0, len = 0, ret, jarray = [];
-
-                // is there enough data in the buffer for this request
-                for (i = 0; i < filebuf.length; i++) {
-                    //tail is how much more we need from the next array
-                    tail = size - len;
-                    len += filebuf[i].length;
-                    endbuf = i;
-                    if (len >= size) break;
-                }
-                if (len < size)
-                    //return undefined, just like shift above
-                    return;
-
-                if (endbuf == 0) {
-                    // the easy case
-                    ret = filebuf[0].subarray(0, size);
-                    filebuf[0] = filebuf[0].subarray(size);
-                    this.slice += size;
-                    this.read += size;
-                    this.length = this.written - this.read;
-                    return ret;
-                }
-
-                for (i = 0; i < endbuf; i++)
-                    jarray.push(filebuf.shift());
-
-                jarray.push(filebuf[0].subarray(0, tail));
-
-                filebuf[0] = filebuf[0].subarray(tail);
-                this.slice = tail;
-                ret = joinarray(jarray);
-                this.read += ret.length;
-                this.length = this.written - this.read;
-                return ret;
-            },
-
-            // Just like get, but don't advance or update read
-            // size=-1:  get all data as one array
-            // size>0:   shift size data from buffer.  if buffer smaller than size, return undefined
-            // size==undefined:  get a convenient amount of data out of the buffer;  if no data left, return undefined
-            preview: function (size) {
-                var filebuf = this.buffers;
-                this.length = this.written - this.read;
-                if (!filebuf || !filebuf.length) return;
-                // get the first block of data if undefined, or if that was the size requested
-                if (size === undefined || size == filebuf[0].length) {
-                    this.bufslice = 0;
-                    return (filebuf[0]);
-                }
-                //return everything if -1
-                //if no data in buffer, return undefined;
-                if (size == -1)
-                    return joinarray(this.buffers);
-
-                var endbuf = 0, tail;
-                var i = 0, len = 0, ret, jarray = [];
-
-                // is there enough data in the buffer for this request
-                for (i = 0; i < filebuf.length; i++) {
-                    //tail is how much more we need from the last array
-                    //from which we will grab data
-                    tail = size - len;
-                    len += filebuf[i].length;
-                    endbuf = i;
-                    if (len >= size) break;
-                }
-                if (len < size)
-                    //return undefined, just like shift above
-                    return;
-
-                if (endbuf == 0)
-                    return filebuf[0].subarray(0, size);
-
-                for (i = 0; i < endbuf; i++)
-                    jarray.push(filebuf[i]);
-
-                jarray.push(filebuf[i].subarray(0, tail));
-
-                return joinarray(jarray);
-            }
-        }
-    }
-
-    /*  parsefile (file)
-        takes an arraybuffer or uint8array of encrypted file and returns position of items in head
-        and start of the body
-        also checks format of file and returns errors;
-    */
-
-    var parsefile = function (file) {
-        var bufi, credblock, credext, credextsize, head, body;
-
-        //make sure we have a uint8array
-        file = toU8(file);
-
-        if (file === undefined)
-            return { data: file, error: "bad input" };
-
-        //check first 4 bytes
-        if (!(file[0] == 0x41 && file[1] == 0x45 && file[2] == 0x53)) {
-            //return error
-            //console.log("bad magic");
-            return { data: file, error: "bad magic" }
-        }
-
-        if (file[3] != 2) {
-            //return error
-            //console.log("wrong file version, only supports v2");
-            return { data: file, error: "wrong file version, only supports v2" }
-        }
-
-        //find our extension, skip past rest
-        bufi = 5;
-        while (file[bufi] != 0 || file[bufi + 1] != 0) {
-            var sig, size = (file[bufi] << 8) + file[bufi + 1];
-            if (size > 17) {
-                sig = arrayToString(file.subarray(bufi + 2, bufi + 17));
-                if (sig == "enckeyblk v 0.1") {
-                    credext = bufi + 18;
-                    credextsize = size - 16;
-                }
-                bufi += size + 2;
-            }
-            if (bufi > file.length) {
-                //return error
-                //console.log("error finding end of extensions");
-                return { data: file, error: "error finding end of extensions" };
-            }
-        }
-
-        bufi += 2;
-        credblock = bufi;
-        return { data: file, credblock: credblock, credext: credext, credextsize: credextsize, datastart: credblock + 96, error: '' };
-    }
-    /** Delete passwords from the file, Not part of the aescrypt 02 format standard */
-
-    /**
-     * Delete passwords from aescrypt.js extended aescrypt 02 formatted file.
-     *
-     * Behavior differs from other functions.  If error, still returns the encoded file back unaltered.
-     *
-     * @param encfile the bytes to encrypt (either encoded as String, one byte per
-     *          character, or as an ArrayBuffer or a Uint8Array).
-     *
-     * @param pass password String that was used for encryption 
-     *
-     * @param delblockarray array of ints between 0-15 specifying password-encrypted key slots to delete
-     *                  or array of strings containing passwords of passwords-encrypted key to delete.
-     *
-     * @param requirepass whether to use password to confirm ownership of file
-     *	              If set to false, blocks will be erased without confirming that pass will decrypt file
-     */
-
-    var delpass = function (encfile, pass, delblockarray, requirepass) {
-        var file, error, newblock, emptyblock,
-            key = "", encdata, data, i = 0, index = [], fileinfo
-        var blankarray = new Uint8Array(96);
-
-
-        //sanity check
-        if (toType(delblockarray) == 'array') {
-            var isnum = false;
-            var isstring = false;
-            for (var i = 0; i < delblockarray.length; i++) {
-                var x = delblockarray[i];
-                if (Math.round(x) == x) {
-                    if (x > -1 && x < 682)
-                        isnum = true;
-                    //in case someone has a password of all digits (baaaaaad), lets hope the number is less than 683
-                    else
-                        isstring = true;
-                }
-                //we'll skip empty strings below
-                else if (x != '')
-                    isstring = true;
-            }
-            // we will only handle an array of all strings or all numbers
-            if ((isnum && isstring) || (!isnum && !isstring))
-                return { data: file, error: "delblockarray must be an array of all numbers between 0 and 682 inclusive, or an array of all password strings" }
-        } else {
-            return { data: file, error: "delblockarray is not an array" };
-        }
-
-        //{data:file, credblock: credblock, credext: credext, credextsize: credextsize, datastart: credblock+96, error: ''}
-        //end of extensions tag (0x0000) is at credblock-2;
-        //begin of extended credblock extension, including size bytes is at credext-18;
-
-        fileinfo = parsefile(encfile);
-        if (fileinfo.error != '') return fileinfo;
-
-        file = fileinfo.data;
-        if (file === undefined)
-            return { data: file, error: "bad input" };
-
-        if (fileinfo.credext === undefined) {
-            //Nothing to do here
-            return { data: file, error: "" };
-        }
-
-        // default is to require password
-        if (requirepass !== false) {
-            key = getanykey(file, pass, false, fileinfo.credext - 18);
-            if (key.error != "")
-                return { data: file, error: key.error };
-        }
-
-        if (isstring) {
-            //find blocks matching passwords and delete it;
-            var x = [];
-            for (var i = 0; i < delblockarray.length; i++) {
-                var key = getanykey(file, delblockarray[i], true, fileinfo.credext - 18, true);//second true means delete password
-                if (key.error == '') {
-                    x.push(key.index);
-                }
-            }
-            return { data: file, error: "", index: x };
-        }
-
-        // doesn't actually check for password, only deletes.
-        var key = getanykey(file, '', true, fileinfo.credext - 18, delblockarray);
-
-        return { data: file, error: "", index: key.index };
-    }
-
-
-
-    /** Add another password to the file, Not part of the aescrypt 02 format standard */
-
-    /**
-     * Add Password to aescrypt.js extended aescrypt 02 formatted file
-     *
-     * Behavior differs from aes[en|de]crypt.  On error, return unaltered encrypted file
-     *
-     * @param encfile the bytes to encrypt (either encoded as String, one byte per
-     *          character, or as an ArrayBuffer or a Uint8Array).
-     *
-     * @param pass password String that was used for encryption 
-     *
-     * @param newpass password String to add to this file. 
-     */
-
-    var addpass = function (encfile, pass, newpass) {
-        var file, error, newblock, emptyblock, fileinfo,
-            key, i = 0, j = 0, index;
-
-        fileinfo = parsefile(encfile);
-        if (fileinfo.error != '') return fileinfo;
-
-        //{data:file, credblock: credblock, credext: credext, credextsize: credextsize, datastart: credblock+96, error: ''}
-        //end of extensions tag (0x0000) is at credblock-2;
-        //begin of extended credblock extension, including size bytes is at credext-18;
-
-        file = fileinfo.data;
-
-        // get a (the) key using this password.  Since we know the start of the extended credential extension, skip to that position
-        key = getanykey(file, pass, false, fileinfo.credext - 18);
-        if (key.error != "")
-            return { data: file, error: key.error };
-
-        newblock = newcredentialblock(newpass, key.key, key.fileiv);
-        if (newblock == "") return { data: file, error: "unable to make new password entry for file" };
-
-        if (fileinfo.credext === undefined) {
-            //we have to write a new file with room for extended credential block with 16 key slots (18 + (16*96))==1554
-            var endofext = fileinfo.credblock - 2;
-            var newfile = new Uint8Array(file.length + 1554);
-            // copy all of head up to but not including end of extension tag (0x0000)
-            copyToArray(file, newfile, 0, endofext);
-            //create a new credential block using newpass
-            //create entry for extended credential extension
-            copyToArray((String.fromCharCode(6) + String.fromCharCode(16) + "enckeyblk v 0.1" + String.fromCharCode(0)),
-                newfile, endofext);
-            //copy in the new credential block
-            copyToArray(newblock.buffer, newfile, endofext + 18);
-            //copy rest of data from original file starting with the end of extensions tag 0x0000
-            copyToArray(file.subarray(endofext), newfile, endofext + 1554);
-            return { data: newfile, index: 0, error: "" };
-        }
-
-
-        //find first empty block
-        j = 0;
-        for (var i = 0; i < fileinfo.credextsize; i += 96) {
-            var start = i + fileinfo.credext;
-            if (isblank(file, start, 96)) {
-                emptyblock = start;
-                index = j;
-                break;
-            }
-            j++;
-        }
-        // TODO: extend it again if full.
-        if (emptyblock === undefined)
-            return { data: file, error: "error: all keyslots are in use" };
-
-        copyToArray(newblock.buffer, file, emptyblock);
-        return { data: file, index: index, error: "" };
-    }
-
-    /** Decrypt data in the aescrypt 02 format (ver 1 and 0 not supported) */
-
-    /**
-     * Decrypt encfile using password where encfile is in aescrypt 02 format
-     * Return an ArrayBuffer of the plaintext file or a binary string
-     * 
-     *
-     * @param encfile the bytes to encrypt (either encoded as String, one byte per
-     *          character, or as an ArrayBuffer or Typed Array).
-     *
-     * @param pass password String that was used for encryption 
-     *
-     * @param returnstring boolean where if true function returns a binary string
-     *
-     * @param cb, callback with results
-     */
-
-    var aesdecrypt = function (encfile, pass, returnstring, cb) {
-        var emptydata = new Uint8Array(0);
-        //var usemod=true;
-
-        //take a uint8array, arraybuffer or string for file
-        var file = toU8(encfile);
-        if (file === undefined)
-            return { data: emptydata, error: "bad input" };
-
-
-        /*         testing: 
-        {
-        //var filebuf=newbytebuf(file);		
-        //or 
-        var filebuf=newbytebuf(file).done();
-
-        var key=decryptparsehead( filebuf, pass );
-        var decryptor=decryptstart(key);
-        // end of string is set with .done() above, so only need one pass in decryptpayload();
-        //var decrypted=decryptpayload(filebuf,decryptor);
-        //	while (decrypted>0) decrypted=decryptpayload(filebuf,decryptor);
-        //filebuf.done();
-        var decrypted=decryptpayload(filebuf,decryptor);
-        var error=decryptfinish(filebuf,decryptor);
-        }
-
-        console.log("test over");
-        */
-
-        var key = decrypthead(file, pass);
-        if (key.error != '') return { data: emptydata, error: key.error };
-
-        if (crypto && crypto.subtle && typeof cb == 'function') {
-            var cr = crypto.subtle;
-
-            function checkhmac(dec) {
-                cr.importKey(
-                    'raw',
-                    toU8(key.key),
-                    { name: "HMAC", hash: { name: "SHA-256" } },
-                    false,
-                    ["sign", "verify"]
-                ).then(function (k) {
-                    cr.verify(
-                        { name: "HMAC" },
-                        k,
-                        file.subarray(-32),
-                        file.subarray(key.datastart, -33)
-                    ).then(function (valid) {
-                        if (valid) cb({ data: dec, error: '' });
-                        else cb({ data: dec, error: "hmac does not match. Likely file corruption or tampering." })
-                    }).catch(function (e) {
-                        //console.log(e);
-                        cb({ data: emptydata, error: e.message });
-                    });;
-                }).catch(function (e) {
-                    //console.log(e);
-                    cb({ data: emptydata, error: e.message });
-                });
-
-            }
-            cr.importKey(
-                'raw',
-                toU8(key.key),
-                { name: "AES-CBC" },
-                false,
-                ["encrypt", "decrypt"]
-            ).then(function (k) {
-                var modbyte = file[file.length - 33];
-                var subfile = file.subarray(key.datastart, -33);
-                //console.log(modbyte);
-                if (modbyte == 0) {
-                    //we can cheat our way out of the fact that the aescrypt file format fails to put padding on
-                    //a size%16==0 sized file with forge api, but not with webcrypto api
-                    //so just use forge instead.
-                    cb(aesdecrypt(encfile, pass, returnstring));
-                    return;
-                }
-
-                cr.decrypt(
-                    { name: "AES-CBC", iv: toU8(key.fileiv) },
-                    k,
-                    subfile
-                ).then(function (decrypted) {
-                    decrypted = (new Uint8Array(decrypted)).subarray(0, (modbyte - 16));
-                    checkhmac(decrypted);
-                }).catch(function (e) {
-                    //for (var x in e)
-                    //	console.log(x+'='+e[x]);
-                    // in case we choke somewhere.  Orignally was for above mentioned aescrypt padding problem.
-                    cb(aesdecrypt(encfile, pass, returnstring)); //try with forge
-                })
-                    ;
-            }).catch(function (e) {
-                //console.log(e);
-                cb({ data: emptydata, error: e.message });
-            });
-            return false;
-        }
-
-        var decryptor = decryptstart(key);
-
-        var decrypted = decryptpayload(file, decryptor, key.datastart);
-
-        if (decrypted == -1) return { data: new Uint8Array(0), error: "file corrupted (invalid length)" };
-
-        var error = decryptfinish(file, decryptor, true);
-        if (returnstring)
-            return { data: decryptor.decipher.output.data, error: error };
-        else {
-            var a = new Uint8Array(decryptor.decipher.output.data.length);
-            copyToArray(decryptor.decipher.output.data, a);
-            return { data: a, error: error };
-        }
-    }
-
-    // do head in one go
-    function decrypthead(encfile, pass) {
-        var file, fileinfo, key;
-
-        //{data:file, credblock: credblock, credext: credext, credextsize: credextsize, datastart: credblock+96, error: ''}
-        //end of extensions tag (0x0000) is at credblock-2;
-        //begin of extended credblock extension, including size bytes is at credext-18;
-
-        fileinfo = parsefile(encfile);
-        if (fileinfo.error != '') return fileinfo;
-
-        file = fileinfo.data;
-        if (file === undefined)
-            return { data: file, error: "bad input" };
-        if (fileinfo.credext) {
-            key = getanykey(file, pass, false, fileinfo.credext - 18);
-        } else if (fileinfo.credblock) {
-            key = getkey(file.subarray(fileinfo.credblock, fileinfo.credblock + 96), pass);
-        }
-        else return ({ error: "Could not parse encrypted file" });
-        key.datastart = fileinfo.datastart;
-
-        return key;
-
-    }
-
-    // parse head of file and get key
-    // can be done in stages as more data is put into filebuf
-    // cant use parsefile() if we really want to be able to handle incoming a byte at a time
-    function decryptparsehead(filebuf, pass, progress) {
-        if (progress === undefined) progress = { stage: 0 };
-        //check first 5 bytes
-        if (progress.stage == 0) {
-            //need 5 bytes for stage 1;
-            var file = filebuf.get(5);
-            if (file == undefined) return progress;
-
-            if (!(file[0] == 0x41 && file[1] == 0x45 && file[2] == 0x53)) {
-                //return error
-                //console.log("bad magic");
-                return { error: "bad magic" }
-            }
-
-            if (file[3] != 2) {
-                //return error
-                //console.log("wrong file version, only supports v2");
-                return { error: "wrong file version, only supports v2" }
-            }
-            progress.stage = 1;
-        }
-        if (progress.stage == 1) {
-            file = filebuf.preview(2);
-            if (file === undefined) return progress;
-            while (file[0] != 0 || file[1] != 0) {
-                var sig = '';
-                var extsize = (file[0] << 8) + file[1] + 2;
-                var lengthbytes = file;
-                file = filebuf.get(extsize);
-                if (file === undefined) return progress;
-                // look for extended credential block
-                if (file.length > 17) {
-                    sig = arrayToString(file.subarray(2, 17));
-                    if (sig == "enckeyblk v 0.1") {
-                        var k = getanykey(file, pass, true, 0);
-                        if (k.error == "") progress.key = k;
-                    }
-                }
-                file = filebuf.preview(2);
-                if (file === undefined) return progress;
-            }
-            file = filebuf.get(2); //skip past the final 0x0000
-            progress.stage = 2;
-        }
-        if (progress.stage == 2) {
-            // this is our main credential block containing 
-            // keyiv, encrypted key and file iv and hmac of ( encrypted key + file iv)
-            file = filebuf.get(96);
-            if (file === undefined) return progress;
-            if (progress.key) {
-                progress.key.stage = 3;
-                return progress.key
-            } else {
-                var key = getkey(file, pass);
-                key.stage = 3;
-                return key;
-            }
-            //this is the end of the header section
-            //encrypted data is next
-        }
-    }
-
-
-    // set up decipher, hmac and an object to hold them an output array
-    function decryptstart(key) {
-        var decipher = forge.cipher.createDecipher('AES-CBC', key.key);
-        var hmac = forge.hmac.create();
-        decipher.start({ iv: key.fileiv });
-        hmac.start('sha256', key.key);
-        return { decipher: decipher, hmac: hmac, data: newbytebuf() };
-    }
-
-    // decrypt 64 bytes at a time
-    // either the buffer will need to contain the whole file, or we will
-    // probably need to feed buffer 128 bytes  at a time until the end of file, or it will all break down
-    // if position is defined, we assume file is a uint8array and data starts at pos
-    function decryptpayload(filebuf, decryptor, pos) {
-
-        var block;
-        var len = filebuf.length;
-        // 49 bytes is the minimum payload size (16 bytes encrypted data + 1 mod byte + 32 byte hmac)
-        // if we are at the end of file, just encrypt what we have left.
-        // if filebuf is an uint8array, pos will/should be defined
-        if (filebuf.eof || pos !== undefined) {
-            //console.log("got eof, doing rest of file, len="+len);
-
-            //this should only be 33 or more.
-            if (len < 33) return -1;
-            if (len == 33) return 0;
-            //get all but the last 33 bytes
-            len -= 33;
-            if (pos !== undefined)
-                block = filebuf.subarray(pos, -33);
-            else
-                block = filebuf.get(len);
-            //console.log("starting decrypt");
-            decryptor.decipher.update(forge.util.createBuffer(block));
-            //decryptor.hmac.update(arrayToString(block));
-            //console.log("starting hmac calc");
-            hmacchunkblock(decryptor.hmac, block);
-            //console.log("done with decryption");
-            return (len);
-
-            // if no end of file, then leave at least 33 bytes after this round
-        } else if (len > 97) {
-            //console.log("no eof, leaving at least 33 in buf");
-            len = len - 33;
-            len = len - len % 64
-            //console.log("decrypting "+len+" bytes")			
-            block = filebuf.get(len);
-            //now minimum in buffer is 33
-            decryptor.decipher.update(forge.util.createBuffer(block));
-            //decryptor.hmac.update(arrayToString(block));
-            hmacchunkblock(decryptor.hmac, block);
-            return (len);
-        }
-        return 0;
-    }
-
-    // if pos == true, filebuf is uint8array
-    function decryptfinish(filebuf, decryptor, pos) {
-        var hdat, hdatcomp, modbyte;
-
-        if (pos !== true) {
-            modbyte = filebuf.get(1);
-            if (modbyte === undefined) return "file corrupted (no modbyte)";
-            modbyte = modbyte[0];
-            hdat = arrayToString(filebuf.get(-1));
-        }
-        else {
-            pos = filebuf.length - 33;
-            modbyte = filebuf[pos];
-            hdat = arrayToString(filebuf.subarray(pos + 1));
-        }
-
-        if (!hdat || hdat.length != 32) return "file corrupted (invalid hmac block)";
-
-        hdatcomp = decryptor.hmac.digest().data
-        if (hdat != hdatcomp) {
-            return "hmac does not match. Likely file corruption or tampering.";
-        }
-        if (modbyte != 0)
-            decryptor.decipher.output.data = decryptor.decipher.output.data.slice(
-                0, (modbyte - 16)
-            );
-        return "";
-    }
-
-
-
-
-    /** Encrypt data in the aescrypt 02 format */
-
-    /**
-     * Encrypts filecontents with pass and returns an ArrayBuffer containing a file encrypted in the aescrypt format 02.
-     *
-     * @param filecontents the bytes to encrypt (either encoded as String, one byte per
-     *	  character, or as an ArrayBuffer or Typed Array).
-     *
-     * @param pass password String used for encryption 
-     *
-     * @param returnstring boolean if true return a binary string instead of a uint8arrray
-     *
-     * @param slotn number of slots for extra passwords (default 16)
-     *
-     * @param cb callback to receive encrypted data
-     */
-
-    var aesencrypt = function (filecontents, pass, returnstring, slotn, cb) {
-
-        var emptydata = new Uint8Array(0);
-
-        if (crypto && crypto.subtle && typeof cb == 'function') {
-            var cr = crypto.subtle;
-            var sd = encryptstart(pass, slotn, true);
-            console.log("WEBCRYPTO");
-            function cryptofinish(enc, hdat, head, mod) {
-                var output, bufi,
-                    modbyte = String.fromCharCode(mod);
-
-                output = newbytebuf();
-                output.put(head);
-                output.put(enc);
-                output.put(modbyte);
-                output.put(hdat);
-
-                output = output.get(-1);
-                //console.log(output.length);
-                return { data: output, error: "" };
-            }
-
-            function makehmac(enc) {
-                cr.importKey(
-                    'raw',
-                    toU8(sd.key),
-                    { name: "HMAC", hash: { name: "SHA-256" } },
-                    false,
-                    ["sign", "verify"]
-                ).then(function (k) {
-                    //console.log("key imported for hmac");
-                    var mod = filecontents.length % 16;
-                    if (mod == 0) enc = enc.subarray(0, -16);
-                    cr.sign(
-                        { name: "HMAC" },
-                        k,
-                        enc
-                    ).then(function (sig) {
-                        //console.log("finishing file");
-                        cb(cryptofinish(enc, sig, sd.head, mod));
-                    })/*.catch(function(e) {
-						console.log(e);
-						cb({data:emptydata,error:e.message});
-					});*/;
-                }).catch(function (e) {
-                    //for (var x in e)
-                    //	console.log(x+'='+e[x]);
-                    cb({ data: emptydata, error: e.message });
-                });
-
-            }
-
-            cr.importKey(
-                'raw',
-                toU8(sd.key),
-                { name: "AES-CBC" },
-                false,
-                ["encrypt", "decrypt"]
-            ).then(function (k) {
-                cr.encrypt(
-                    { name: "AES-CBC", iv: toU8(sd.fileiv) },
-                    k,
-                    toU8(filecontents)
-                ).then(function (encrypted) {
-                    //console.log(encrypted.byteLength);
-                    makehmac(new Uint8Array(encrypted));
-                }).catch(function (e) {
-                    //for (var x in e)
-                    //	console.log(x+'='+e[x]);
-                    cb(aesencrypt(filecontents, pass, returnstring, slotn)); //try with forge
-                })
-                    ;
-            }).catch(function (e) {
-                //console.log(e);
-                cb({ data: emptydata, error: e.message });
-            });
-            return false;
-        }
-        // the forge version
-        var startdata = encryptstart(pass, slotn);
-        var mod = encryptupdate(startdata.cipher, startdata.hmac, filecontents);
-        mod %= 16;
-        return encryptfinish(startdata.cipher, startdata.hmac, startdata.head, mod, returnstring);
-    }
-
-    //* set up file header and return keys and iv
-    function encryptstart(pass, extrakeyslots, noforge) {
-        var headstart = new Uint8Array(5);
-        var endext = new Uint8Array(2);//0x0000 for no more extensions
-        var extensions = [];
-        var bufarray = new ArrayBuffer(96);
-        var bufi = 0;
-        var buffer = new Uint8Array(bufarray);
-        var cred = '';
-        var keyiv, extlen = 0, output,
-            i = 0, //blank=new String;
-            blank;
-        var emptydata = new ArrayBuffer(0);
-        var cipher, hmac, extrapasses = [];
-        //console.log(toType(pass));
-        if (toType(pass) == 'array') {
-            extrapasses = pass;
-            pass = extrapasses.shift();
-            //console.log(pass);
-            //console.log(extrapasses);
-        }
-
-
-        // TODO: support for other than 16, and for extending a block that is too small to fit another 
-        //       password (in addpass()).  Right now, a code review of other projects would be required
-        //       since they might rely on file header size being constant (dunno, hence need for review).
-        // if (extrakeyslots===undefined) 
-        extrakeyslots = 16;
-        // max size of extension is 65536
-        if (extrakeyslots > 682) extrakeyslots = 682;
-        copyToArray("AES", headstart, 0);
-        headstart[3] = 2;
-
-        //headstart[4]=0; //aready 0;
-
-        // make credentials
-        //this is now mostly solved and should be successful on every attempt
-        var i = 0;
-        while (cred == '' && i++ < 9) {
-            //if(i>1) console.log("FAILED making new credential block");
-            cred = newcredentialblock(pass);
-        }
-        if (cred == '') {
-            //console.log("tried 8 time, bailing...");
-            return { head: emptydata, error: "could not make a new set of credentials" };
-        }
-
-        /* **** add extensions here **** */
-
-        extensions.push(makeext(["CREATED_BY", "aescrypt.js 0.1"]));
-
-        //add blank 128 byte extension
-
-        blank = new Uint8Array(130);
-        blank.set([128], 1);
-        extensions.push(blank);
-
-        //add extended credential block
-        //and add extra passwords, if available;
-        if (extrakeyslots > 0) {
-            var csize = 16 + (96 * extrakeyslots);
-            var h = String.fromCharCode(csize >>> 8) + String.fromCharCode(csize & 0xFF) + "enckeyblk v 0.1" + String.fromCharCode(0);
-            blank = new Uint8Array(csize + 2);
-            copyToArray(h, blank);
-            var len = (extrapasses.length < 16) ? extrapasses.length : 16;
-            for (var i = 0; i < len; i++) {
-                var newpass = extrapasses[i],
-                    newblock = newcredentialblock(newpass, cred.key, cred.fileiv),
-                    start = 18 + (i * 96);
-
-                copyToArray(newblock.buffer, blank, start);
-            }
-            extensions.push(blank);
-        }
-
-        /*
-        blank=String.fromCharCode(0)+String.fromCharCode(128);
-        for (var i=0;i<128;i++)
-            blank+=String.fromCharCode(0);
-        extensions.push(blank);	
-    	
-        //add an area for 16 96-byte blocks for extra keyiv-enckeyblk-hmac combos ("credential blocks")
-        blank=String.fromCharCode(06)+String.fromCharCode(16)+"enckeyblk v 0.1"+String.fromCharCode(0);
-        for (var i=0;i<1536;i++)
-            blank+=String.fromCharCode(0);
-        extensions.push(blank);
-        */
-
-
-        /* **** end add extensions  **** */
-
-        //the last extension  - 0x0000 to mark end of extensions
-        extensions.push(endext);
-
-        // get length of all extensions
-        for (var i = 0; i < extensions.length; i++)
-            extlen += extensions[i].length;
-
-        output = new Uint8Array(
-            headstart.length +
-            extlen +
-            cred.buffer.length
-        );
-
-        bufi = 0;
-        bufi += copyToArray(headstart, output, bufi);
-        for (var i = 0; i < extensions.length; i++)
-            bufi += copyToArray(extensions[i], output, bufi);
-        bufi += copyToArray(cred.buffer, output, bufi);
-
-        if (!noforge) {
-            cipher = forge.cipher.createCipher('AES-CBC', cred.key);
-            cipher.start({ iv: cred.fileiv });
-
-            hmac = forge.hmac.create();
-            hmac.start('sha256', cred.key);
-            return { head: output, error: "", cipher: cipher, hmac: hmac };
-        }
-
-        return { head: output, error: "", fileiv: cred.fileiv, key: cred.key };
-    }
-
-    function encryptupdate(cipher, hmac, data, newiv) {
-        var encdata;
-        // newiv is a misnomer since cipher.start({iv:newiv}) doesn't work here (not sure why)
-        if (newiv !== false && newiv !== undefined) {
-            //cipher.start({iv:newiv});
-            cipher.output.data = newiv;
-            cipher.update(forge.util.createBuffer(data));
-            cipher.output.data = cipher.output.data.slice(16);
-        }
-        else
-            cipher.update(forge.util.createBuffer(data));
-
-        hmac.update(cipher.output.data);
-
-        return (data.length);
-    }
-
-    function encryptfinish(cipher, hmac, head, mod, returnstring) {
-        var output, len, bufi, hdat,
-            modbyte = String.fromCharCode(mod),
-            hlen = cipher.output.data.length;
-
-        cipher.finish();
-        if (mod != 0)
-            len = cipher.output.data.length;
-
-        // don't pad file that otherwise fits exactly into 16 byte blocks.
-        // forge needs the extra room for encoding a padding number in the plaintext (Section 10.3 of [RFC2315], step 2)
-        // aescrypt format has the padding number outside the encrypted text.
-        else
-            len = cipher.output.data.length - 16;
-
-        //how much extra data left to hmac, negative number or zero
-        hlen -= len;
-
-        //update our hmac with extra data
-        if (hlen < 0)
-            hmac.update(cipher.output.data.slice(hlen));
-
-        hdat = hmac.digest().data;
-
-
-        if (returnstring) {
-            // do not use with chunking
-            return {
-                data: ("").concat(arrayToString(head), cipher.output.data.slice(0, len), modbyte, hdat),
-                error: ""
-            }
-        } else {
-            output = new Uint8Array(
-                head.length +
-                len +
-                modbyte.length +
-                hdat.length
-            );
-            // if getChunk is used below, head will be "" and cipher.output.data
-            // will have been moved off and also be "", or a tail portion of it
-            bufi = 0;
-            bufi += copyToArray(head, output, bufi);
-            bufi += copyToArray(cipher.output.data, output, bufi, len);
-            bufi += copyToArray(modbyte, output, bufi);
-            bufi += copyToArray(hdat, output, bufi);
-            return { data: output, error: "" };
-        }
-    }
-
-
-    var chunkencrypt = function (pass) {
-        return {
-            start: function (pass) {
-                var encstart = encryptstart(pass);
-                this.hmac = encstart.hmac;
-                this.cipher = encstart.cipher;
-                this.head = arrayToString(encstart.head);
-                this.newiv = false;
-                this.length = 0;
-                //there really should be no errors
-                this.error = encstart.error;
-            },
-            update: function (data) {
-                this.length += encryptupdate(this.cipher, this.hmac, data, this.newiv);
-                this.newiv = false;
-                return this;
-            },
-            getChunk: function (size) {
-                //return size must be a multiple of size
-
-                var ret, retsize;
-
-                if (this.leftover) {
-                    this.cipher.output.data = this.leftover + this.cipher.output.data;
-                    delete this.leftover;
-                }
-
-                retsize = parseInt((this.head.length + this.cipher.output.data.length) / size) * size;
-                //console.log("retsize="+retsize+ " or "+size+ ' * ' + parseInt( (this.head.length+this.cipher.output.data.length)/size ) );
-
-                if (retsize == 0) return;
-
-                if (retsize == this.head.length) {
-                    ret = this.head;
-                } else if (retsize < this.head.length) {
-                    ret = this.head.slice(0, retsize);
-                    this.leftover = this.head.slice(retsize);
-                } else {
-                    // concat head to cipher output to make desired size
-                    if (this.head.length) {
-                        retsize -= this.head.length;
-                        ret = this.head + this.cipher.output.data.slice(0, retsize);
-                    } else {
-                        ret = this.cipher.output.data.slice(0, retsize);
-                    }
-
-                    this.leftover = this.cipher.output.data.slice(retsize); //just add this on next time.
-                    this.newiv = this.cipher.output.data.slice(-16); //put 16 bytes back in mix for next cbc mode computation
-                    this.cipher.output.data = "";
-                }
-
-                this.head = '';
-                var reta = new Uint8Array(ret.length);
-                copyToArray(ret, reta);
-                return { data: reta, error: "" };
-
-
-                /*-------------------------- old way------------------------------------------
-                ret = this.head + 
-                        this.cipher.output.data;
-            	
-                // only update if we at least as much data as requsted
-                if (size && size > ret.length)
-                    return;		
-                    	
-                var reta=new Uint8Array(ret.length);
-
-                if( ret.length>0)
-                    copyToArray(ret,reta);
-                else 
-                    return;
-            	
-                // cbc mode needs last 16 bytes to compute next 16
-                // and we'll make adjustments in next update()
-                if (this.cipher.output.data.length) {
-                    this.newiv=this.cipher.output.data.slice(-16);
-                    this.cipher.output.data="";
-                }
-
-                this.head="";
-                            	
-                return {data: reta, error: ""};
-                ----------------------------*/
-            },
-            finish: function () {
-                if (this.leftover) {
-                    this.cipher.output.data = this.leftover + this.cipher.output.data;
-                    delete this.leftover;
-                }
-                var f = encryptfinish(this.cipher, this.hmac, this.head, (this.length % 16));
-                return f;
-            },
-        }
-    };
-
-    /*
-    var aesdecrypt=function(encfile,pass,returnstring) {
-        var emptydata=new ArrayBuffer(0);
-        //var usemod=true;
-
-        //take a uint8array, arraybuffer or string for file
-        file=toU8(encfile);
-        if (file===undefined)
-            return {data:emptydata, error:"bad input"};
-        var filebuf=newbytebuf(file).done();
-        var key=decryptparsehead( filebuf, pass );
-        if (key.error!='') return {data:emptydata, error: key.error};
-        var decryptor=decryptstart(key);
-    	
-        // end of string is set with .done() above, so only need one pass in decryptpayload();
-        var decrypted=decryptpayload(filebuf,decryptor);
-        if (decrypted == -1) return {data:new Uint8Array(0),error:"file corrupted"};
-    	
-        var error=decryptfinish(filebuf,decryptor);
-        if(returnstring)
-            return {data:decryptor.decipher.output.data, error: error};
-        else {
-            var a=new Uint8Array(decryptor.decipher.output.data.length);
-            copyToArray(decryptor.decipher.output.data,a);
-            return {data: a, error: error};
-        }
-    }
-*/
-    //data is not required in start;
-    var chunkdecrypt = function () {
-        return {
-            start: function (pass) {
-                if (!pass || pass == "") {
-                    this.error = "password missing";
-                    return this;
-                }
-                this.pass = pass;
-                this.progress = { stage: 0 };
-                this.filebuf = newbytebuf();
-                this.output = newbytebuf();
-                this.error = "";
-                this.lasterror = "";
-                delete this.decryptor;
-            },
-            update: function (data) {
-                // go no further on unrecoverable error. error message should be in this.error;
-                this.newoutput = false;
-                if (this.progress.stage == -1)
-                    return this;
-
-                // push our data into the buffer
-                this.filebuf.put(data);
-                if (this.progress.stage < 3)
-                    this.progress = decryptparsehead(this.filebuf, this.pass, this.progress);
-
-                if (this.progress.error) {
-                    this.pass = '';
-                    this.error = this.progress.error;
-                    this.progress.stage = -1;
-                    return this;
-                }
-
-                if (this.progress.stage == 3) {
-                    // we should have our key in this.progress.key
-                    // this stage requires no data from filebuf
-
-                    //dont need password lying around;
-                    delete this.pass;
-                    this.decryptor = decryptstart(this.progress);
-                    this.progress.stage = 4;
-                    //dont need key anylonger
-                    delete this.key;
-                }
-                // kinda unnecessary since stage 3 requires no data
-                // but might want to put error checking into decryptstart some day.
-                if (this.progress.stage == 4) {
-                    // decrypt whatever is in the buffer in multiples of 64 bytes, leaving at least 33 at end
-                    var decrypted = decryptpayload(this.filebuf, this.decryptor);
-                    if (decrypted > 0) {
-                        this.output.put(this.decryptor.decipher.output.data);
-                        this.decryptor.decipher.output.data = "";
-                        this.newoutput = true;
-                    } else if (decrypted == -1) {
-                        this.error = "file corrupted (invalid length)";
-                        this.progress.stage = -1;
-                    }
-                }
-                return this;
-            },
-            getChunk: function (size) {
-                var ret;
-                // get all of the buffer if size undefined
-                if (size === undefined) size = -1;
-                if (size == 0 || !this.output)
-                    return;
-
-                ret = this.output.get(size);
-                // return undefined if no data				
-                if (!ret || ret.length == 0) return;
-
-                if (this.output.eof && this.output.length == 0)
-                    delete this.output;
-
-                return { data: ret, error: this.error };
-            },
-            finish: function () {
-                // mark eof
-                this.filebuf.done();
-                // finish off whatever is in the buffer
-                var decrypted = decryptpayload(this.filebuf, this.decryptor);
-                if (decrypted > 0) {
-                    //check hmac and truncate
-                    this.error = decryptfinish(this.filebuf, this.decryptor);
-                    //copy output
-                    this.output.put(this.decryptor.decipher.output.data);
-                    //empty forge buffer
-                    this.decryptor.decipher.output.data = "";
-                    this.newoutput = true;
-                    this.output.eof = true;
-                } else if (decrypted == -1) {
-                    this.error = "file corrupted (invalid length)";
-                } else if (decrypted == 0) {
-                    delete this.output;
-                }
-
-                this.progress.stage = -1;
-
-                delete this.filebuf;
-                delete this.decryptor;
-                var lastchunk = this.getChunk();
-                if (lastchunk && lastchunk.data)
-                    lastchunk = lastchunk.data;
-                return { data: lastchunk, error: this.error };
-            },
-        }
-    };
-
-
-    return ({
-        chunkEncrypt: chunkencrypt,
-        chunkDecrypt: chunkdecrypt,
-        encrypt: aesencrypt,
-        decrypt: aesdecrypt,
-        addPassword: addpass,
-        delPassword: delpass,
-        util: {
-            copyToArray: copyToArray,
-            arrayToString: arrayToString,
-            toType: toType,
-            newbytebuf: newbytebuf
-        }
-    });
-})();

+ 0 - 21
src/utils/aescrypt/index.ts

@@ -1,21 +0,0 @@
-import { aescrypt } from './aescrypt';
-
-const decrypt = (file: Uint8Array, password: String, binaryResponse: Boolean = false) => {
-    return new Promise((resolve, reject) => {
-        try {
-            aescrypt.decrypt(file, password, !binaryResponse, ({ data, error}) => {
-                if (error) {
-                    console.log(error);
-                    reject(error);
-                }
-                resolve(data);
-            });
-        } catch (e) {
-            reject(e);
-        }
-    });
-}
-
-export default {
-    decrypt,
-}

+ 18 - 19
src/worker/decryptMetadata.worker.js

@@ -1,24 +1,23 @@
+import * as Comlink from 'comlink';
 import { base64ToUint8 } from "utils/crypto/common";
 import sodium from 'libsodium-wrappers';
 
-function decryptMetadata(event) {
-    const main = async () => {
-        const data = event.data.data;
-        await sodium.ready;
-        const key = sodium.crypto_secretbox_open_easy(
-            base64ToUint8(data.metadata.decryptionParams.encryptedKey),
-            base64ToUint8(data.metadata.decryptionParams.keyDecryptionNonce),
-            base64ToUint8(event.data.key));
-        const metadata = sodium.crypto_secretbox_open_easy(
-            base64ToUint8(data.metadata.encryptedData),
-            base64ToUint8(data.metadata.decryptionParams.nonce),
-            key);
-        self.postMessage({
-            ...data,
-            metadata: JSON.parse(new TextDecoder().decode(metadata))
-        });
-    }
-    main();
+async function decryptMetadata(event) {
+    console.log(event);
+    const { data } = event;
+    await sodium.ready;
+    const key = sodium.crypto_secretbox_open_easy(
+        base64ToUint8(data.metadata.decryptionParams.encryptedKey),
+        base64ToUint8(data.metadata.decryptionParams.keyDecryptionNonce),
+        base64ToUint8(event.key));
+    const metadata = sodium.crypto_secretbox_open_easy(
+        base64ToUint8(data.metadata.encryptedData),
+        base64ToUint8(data.metadata.decryptionParams.nonce),
+        key);
+    return {
+        ...data,
+        metadata: JSON.parse(new TextDecoder().decode(metadata))
+    };
 }
 
-self.addEventListener('message', decryptMetadata);
+Comlink.expose(decryptMetadata, self);

+ 17 - 19
src/worker/decryptThumbnail.worker.js

@@ -1,24 +1,22 @@
+import * as Comlink from 'comlink';
 import { base64ToUint8 } from "utils/crypto/common";
 import sodium from 'libsodium-wrappers';
 
-function decryptThumbnail(event) {
-    const main = async () => {
-        const data = event.data.data;
-        await sodium.ready;
-        const key = sodium.crypto_secretbox_open_easy(
-            base64ToUint8(data.thumbnail.decryptionParams.encryptedKey),
-            base64ToUint8(data.thumbnail.decryptionParams.keyDecryptionNonce),
-            base64ToUint8(event.data.key));
-        const thumbnail = sodium.crypto_secretbox_open_easy(
-            new Uint8Array(data.file),
-            base64ToUint8(data.thumbnail.decryptionParams.nonce),
-            key);
-        self.postMessage({
-            id: data.id,
-            data: thumbnail,
-        });
-    }
-    main();
+async function decryptThumbnail(event) {
+    const { data } = event;
+    await sodium.ready;
+    const key = sodium.crypto_secretbox_open_easy(
+        base64ToUint8(data.thumbnail.decryptionParams.encryptedKey),
+        base64ToUint8(data.thumbnail.decryptionParams.keyDecryptionNonce),
+        base64ToUint8(event.key));
+    const thumbnail = sodium.crypto_secretbox_open_easy(
+        new Uint8Array(data.file),
+        base64ToUint8(data.thumbnail.decryptionParams.nonce),
+        key);
+    return {
+        id: data.id,
+        data: thumbnail,
+    };
 }
 
-self.addEventListener('message', decryptThumbnail);
+Comlink.expose(decryptThumbnail, self);

+ 5 - 0
yarn.lock

@@ -2076,6 +2076,11 @@ color-name@~1.1.4:
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
+comlink@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.3.0.tgz#80b3366baccd87897dab3638ebfcfae28b2f87c7"
+  integrity sha512-mu4KKKNuW8TvkfpW/H88HBPeILubBS6T94BdD1VWBXNXfiyqVtwUCVNO1GeNOBTsIswzsMjWlycYr+77F5b84g==
+
 commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"