Search code examples
filereadercryptojs

Progressive Upload and Encryption with CryptoJS


The goal here is to upload a file, encrypt it at the client side, and send the file and its attributes via AJAX to myphpscript.php. To allow larger files, I want to upload in slices using the FileReader slice method and progressively encrypt the slices using the methods described on the CryptoJS site (https://code.google.com/p/crypto-js/). My code below runs, but only ends up storing a a small portion of the intended entire encrypted file. Can I progressively upload and encrypt in the way I am describing?

<script type="text/javascript" src="js/jquery-1.11.0.min.js"></script>
<script src="js/aes.js"></script>
<script>
    function readBlob(opt_startByte, opt_stopByte) {

        var files = document.getElementById('fileinput').files;
        if (!files.length) {
            alert('Please select a file!');
            return;
        }

        var file = files[0];
        var start = parseInt(opt_startByte) || 0;
        var stop = parseInt(opt_stopByte) || file.size - 1;

        var reader = new FileReader();

        // If we use onloadend, we need to check the readyState.
        reader.onloadend = function(evt) {
            if (evt.target.readyState == FileReader.DONE) { // DONE == 2
                window.bits.push(aesEncryptor.process(evt.target.result));
            }
        };

        var blob = file.slice(start, stop + 1);
        reader.readAsBinaryString(blob);
    }

    function handling(evt) {

        // INITIALIZE PROGRESSIVE ENCRYPTION
        var key = CryptoJS.enc.Hex.parse(document.getElementById('pass').value);
        var iv = CryptoJS.lib.WordArray.random(128 / 8);
        window.bits = [];
        window.aesEncryptor = CryptoJS.algo.AES.createEncryptor(key, {iv: iv});

        // LOOP THROUGH BYTES AND PROGRESSIVELY ENCRYPT
        var startByte = 0;
        var endByte = 0;
        while(startByte < document.querySelector('input[type=file]').files[0].size - 1){
            endByte = startByte + 1000000;
            readBlob(startByte, endByte);
            startByte = endByte;
        }

        // FINALIZE ENCRYPTION AND UPLOAD
        var encrypted = aesEncryptor.finalize();
        encrypted = encodeURIComponent(encrypted);
        var filename = document.getElementById('fileinput').value;
        var file_type = document.getElementById('fileinput').files[0].type;
        var url = 'data=' + encrypted + '&filename=' + filename + '&filetype=' + file_type;
        $.ajax({
            url: 'myphpscript.php',
            type: 'POST',
            data: url
        }).success(function(data){
            // Display encrypted data
            document.getElementById('status').innerHTML = 'Upload Complete.';
        });
        alert(encrypted);

    }
</script>

Solution

  • So your problem is the line var encrypted = aesEncryptor.finalize();

    This is not the encrypted file, but the final 'chunk' produced by the CryptoJS.AES finalizer.

    you need to append that to the end of the window.bits buffer to yield the fully encrypted file.

    Also, you shouldn't be using window.bits.push, you should keep hold of a reference to each chunk like this (psuedocode):

    var prog;
    //then in the loop, if chunk is null assign to chunk or else concat:
    loop:
        if(!prog)
            prog = cipher.process()
        else
            prog.concat(cipher.process())
    
    //then finalize
    prog.concat(cipher.finalize())
    
    //now you're free to do whatever with the encrypted file:
    var ciphertext = prog.toString()