I'm trying to encrypt big files (> 1GB) using the WebCrypto API.
Encrypting small files works great but when I try to encrypt big files, my browser hangs and it seems the encryption is never finished.
I think the best option would be to read and encrypt chunks of the file and than merge the chunks together when done reading.
Does anyone here have experience with encrypting and merging chunks together? If so, I would highly appreciate it if someone could show me an example or point me into the right correction.
Thanks in advance!
Obviously the high level WebCrypto API was not designed with streaming / large messages in mind. However, if you look at common cipher modes of operation then you'll find that you can use the different modes in such a way that you can concatenate different ciphertext and get a full ciphertext.
You could use AES-CBC for a 1 MiB chunk (or any multiple of 16 bytes) and use the first-to-last 16 bytes of ciphertext as IV to encrypt the next chunk. You will have to dismiss last block as it contains encrypted padding for any chunk except of course the last. Unfortunately, this is not an integrity / authentication mode and the result is vulnerable against padding oracle attacks, so I would still not recommend it. Also, as you dismiss the padding, you cannot decrypt using the same method.
Similarly, you can use counter (CTR) mode with a counter and start with counter value <nonce>|0000000000000000
and then after 1 MiB continue encrypting with counter value <nonce>|0000000000010000
(as 1 MiB contains 65536 blocks), then use <nonce>|0000000000020000
after 2 MiB etc.. The nonce is a unique 8 byte value in the previous IV / initial counter blocks. This cipher has perfect symmetry between encryption & decryption and doesn't use padding. However, it is very vulnerable against change by an adversary; basically an adversary may flip any byte in the ciphertext and thereby flip any bit in the plaintext. This could also lead to further plaintext oracles and leak additional information.
Beware that you cannot do the same thing for AEAD ciphers such as GCM. In that case you need to expand the ciphertext with an nonce and the authentication tag. But you can still encrypt, say, 1 MiB and expand it to a 12 byte nonce, 1 MiB of ciphertext and a 16 byte authentication tag. Then you can decrypt using that larger block size and get 1 MiB plaintext blocks back. Note that you still have to perform a MAC over the authentication tags, otherwise an attacker may reorder the 1 MiB + 28B sized blocks. One way to do that is to perform a 0 byte GCM encryption using the authentication tags as AAD input.
Whew, I guess that's what you get for using an API outside its original intent. It is perfectly possible, but yeah, some study required I guess.
Using 1 MiB to mean 1024 x 1024 bytes here, or 1024 KiB. Mega means million, not - uh, I again forgot the exact amount.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With