3
$\begingroup$

I want to encrypt user's files on our server. For this purpose I decided to use the PHP Mcrypt library. I chose to use the Rijandel-128 algorithm and CBC mode for encryption. Since files are quite large, I decided to encrypt/decrypt files by chunks.

Everything works well when I use one chunk size for encryption and decryption processes. But when I encrypt a file with 8 KB chunk size and try to decrypt it with 4 KB chunk size, file is corrupted.

My question is – is it possible to encrypt and decrypt a file with different chunk sizes? If yes, what are the pitfalls of this process? Right now, I don't understand if there are any bugs in my code or there are some encryption restrictions I'm not aware of.

$\endgroup$
12
  • $\begingroup$ In general, no, it will not work. What are you trying to gain by doing that? If you explain your requirements, there may be something else we can point you towards to help. In other words, instead of describing a solution to your problem, why not tell us your problem. $\endgroup$
    – mikeazo
    Apr 21, 2014 at 11:43
  • $\begingroup$ Hi, @mikeazo. Thanks for response. I don't have any special requirements about chunk size. I just thought it would be good if code will work correctly for any chunk size and you won't have to store this value. So, if it will not work, what is the best practice to store chunk size that was used during encryption? Also, can you explain why it will not work? I still don't get it. $\endgroup$
    – Tamara
    Apr 21, 2014 at 11:54
  • $\begingroup$ In my software chunk size only refers to how much file data is read into memory before encryption, and does not affect the ciphertext or decryption parameters. I guess this would be specific to Mcrypt or to whatever mode it is using $\endgroup$ Apr 21, 2014 at 12:21
  • 1
    $\begingroup$ If I get some time, I'll write up an explanation of why it doesn't work. I'm curious. Why even split into chunks at all? Does a user ever need to access less than the whole file? $\endgroup$
    – mikeazo
    Apr 21, 2014 at 12:33
  • $\begingroup$ @mikeazo Well, file can be quite large - up to 100 Mb. So, during uploading to the server, I don't want to load whole file into memory and encrypt it. Instead I will read data by chunks, encrypt each chunk and save it. The same situation during downloading file. Memory limitation force me to work with chunks. $\endgroup$
    – Tamara
    Apr 21, 2014 at 12:40

2 Answers 2

5
$\begingroup$

To see the problem, let's see how I would chain up a single function (call it $AES\,CBC_k(iv, m)$) which only encrypts a single block at a time into something that can encrypt "chunks" of any size.

Let $m=m_1||m_2||m_3||m_4$ be the message I want to encrypt. Each $m_i$ is a single block (in AES it is 128 bits). I want to use $AES\,CBC_k$ to encrypt $m$. The proper way to do this would be to compute:

$$c_1=AES\,CBC_k(iv, m_1)$$ $$c_2=AES\,CBC_k(c_1, m_2)$$ $$c_3=AES\,CBC_k(c_2, m_3)$$ $$c_4=AES\,CBC_k(c_3, m_4)$$

Then let $c=c_1||c_2||c_3||c_4$ be the ciphertext. Using this process (note how the iv changes from block to block), we could build another function, say $E_k$ which encrypts messages of any length (actually a multiple of the block size).

To illustrate your problem, let's split $m$ into two chunks: $m_1||m_2$ and $m_3||m_4$ and see what it sounds like you are doing. It sounds like you are calling $E_k(iv, m_1||m_2)$ then $E_k(iv, m_3||m_4)$. This is like doing the following.

$$c_1=AES\,CBC_k(iv, m_1)$$ $$c_2=AES\,CBC_k(c_1, m_2)$$ $$c_3=AES\,CBC_k(iv, m_3)$$ $$c_4=AES\,CBC_k(c_3, m_4)$$

Notice the iv for $c_3$ and compare it with the iv for $c_3$ in the correct operation above.

So, if you really want to encrypt the way you are talking about doing, you would need to use the last block of the previous chunk as the iv for the next chunk. Managing this yourself in code, while do able, is somewhat of a pain. I'm willing to bet there is an encryption library for PHP that can so this for you (on file streams or something so the whole file doesn't have to be in memory).

The real question
So it sounds like your real question is how to encrypt large files in php without having to read the whole thing in to memory. I don't use PHP much, so I can't really comment. I will say don't use the solution here as it was obviously not designed by anyone who knows anything about cryptography. I know in other languages (Java, c#) you can pass in a file stream to be encrypted in most libraries, which probably solves the problem of having to read in all the memory. Not sure if PHP has something like that though.

Update
It looks like php has encryption filters that you could use with stream_filter_append to encrypt streams so that the file doesn't have to be loaded into memory. Then you would have a stream to read from and a stream to write to. Simply read from the input stream and write to the output stream. The encryption happens automatically thanks to the filter.

$\endgroup$
2
  • $\begingroup$ Thanks for detailed answer :) I'll read about encryption filters and stream_filter_append (never worked with them). Writing own solution is not an option (at least at my current level). And maybe I should try another php encryption library. The most important - now I understand why my code is not working! $\endgroup$
    – Tamara
    Apr 21, 2014 at 19:32
  • $\begingroup$ Glad we figured it out. In your learning, please feel free to continue using this site to help you. I'd suggest you read the help center on the types of questions that are on topic here. $\endgroup$
    – mikeazo
    Apr 21, 2014 at 19:40
-1
$\begingroup$

sure, its not going to be decrypted as you use CBC mode. Once you change the chunk size, therefore you change the IV, as result, you cannot decrypt.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.