Score:2

Distinguishing the correct IV from incorrect IV in AES CBC when key is known

us flag

Currently, I'm using a static IV value for all encryption and decryption but I would like it to be dynamic for each encyption/decryption request so I started using new byte[16] and it works. The problem is how to detect and decrypt old data. Below is my code to decrypt and I'm passing static IV stored on a secret in keyvault.

private static byte[] DecryptFromBytes_Aes(byte[] dataBytes, byte[] key, byte[] iV)
    {
        if (dataBytes == null || dataBytes.Length <= 0)
            throw new ArgumentNullException("DataBytes");
        if (key == null || key.Length <= 0)
            throw new ArgumentNullException("key");
        if (iV == null || iV.Length <= 0)
            throw new ArgumentNullException("iV");

        byte[] result = null;
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.Key = key;
            aesAlg.IV = iV;
            aesAlg.Padding = PaddingMode.PKCS7;
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
            using (MemoryStream memoryStream = new MemoryStream(dataBytes))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    byte[] decryptedBytes = new byte[dataBytes.Length];
                    int read = cryptoStream.Read(decryptedBytes, 0, dataBytes.Length);
                    Array.Resize(ref decryptedBytes, read);
                    result = decryptedBytes;
                }
            }

        }

        return result;
    }
Bharat Malhotra avatar
us flag
Thank you for the quick response. I have been told to post it here :)
Bharat Malhotra avatar
us flag
Btw, I'm prepending it but I do not know how to handle for all existing data
Aman Grewal avatar
gb flag
If it's already prepended for the old data, just detect if the IV matches the static one. If it does, decrypt it as normal, but generate a new IV when encrypting (even if all the other data is the same).
kelalaka avatar
in flag
@AmanGrewal the actual problem that I see is this; the static IV is not stored with the ciphertext and the date is not reliable to make the distinction. This simply turns that can we distinguish a CBC ciphertext prepended with a random IV from CBC ciphertext not prepended IV. Checking padding is the key.
Score:4
in flag

Problem is how to detect and decrypt old data. Below is my code to decrypt and I'm passing static IV stored on a secret in keyvault.

The answer relies on the padding and the correctness of the key.

  • Case: If the key is correct and IV not;

    Remember that CBC decryption executed as \begin{align} P_1 =& Dec_k(C_1) \oplus IV\\ P_i =& Dec_k(C_i) \oplus C_{i-1},\;\; 1 < i \leq nb, \end{align}

    Therefore if the IV is not correct, then the first block $P_1$ is not correct. We can consider this similar to bit-flipping attack in the first block. Next blocks $P_i$ will decrypt correctly since they rely on the correctness of $C_i$ and $C_{i-1}$ and in this case, they are correct. Only your first block will be incorrect. Distinguishing this can be hard and you need to prepare some filter to distinguish it from the properties of the plaintext - language or format. Comparing the two cases is needed; is it static IV or random IV. Whichever passes your filter can be the correct case and this really depends on your data.

    If the first block of the plaintext is pure text and contains only characters from ASCII's first part ( i.e. the int value of char < 128 ) then this is a good distinguisher. Just check that each 16 byte < 128.

    Migrate all your files into a unique format as soon as possible.

  • Case: If the key is not correct then you will get a PKCS#7 padding error with a high probability.

    The padding bytes will be one of the following at the end of the plaintext depending on the size of the plaintext. With padding, the size needs to be a minimum multiple of the size of the block of AES, which has a 16-byte block size.

      missing  1 byte - padding bytes : 01
      missing  2 byte - padding bytes : 02 02
      missing  3 byte - padding bytes : 03 03 03
      missing  4 byte - padding bytes : 04 04 04 04
      missing  5 byte - padding bytes : 05 05 05 05 05
      missing  6 byte - padding bytes : 06 06 06 06 06 06
      ...
      full block      - padding bytes : 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 
      Even if the plaintext is a multiple of 128, a full block is needed so that padding is not ambigious.
    

    Check that the padding is correct.

    If not correct, you can be sure that the IV is not correct. If correct, there is a $1/256$ probability that the IV generates a random valid 01 padding, with $1/(256)^2$ probability 0202 valid padding, and so on. To remove this possibility, you need to check the data. The language or any other format about the plaintext may help you to eliminate the false positives.

Manish Adhikari avatar
us flag
Am I missing something or where is it suggested in the question that the ciphertext consists of a single block? If it is more than one block, padding check will always pass, I will only get the corrupted first block, if IV is incorrect.
Manish Adhikari avatar
us flag
I am not the OP. I was just indicating that checking IV via correctness of padding only works if it only produces one single block of ciphertext. So, I was just wondering if there was some subtle suggestion at that which I missed. Thanks for updating your answer
kelalaka avatar
in flag
@ManishAdhikari yes you are not OP :)
Bharat Malhotra avatar
us flag
Thank you for sharing your thoughts on this :) I do not carry this much knowledge to fix this problem so was just wondering if a trick to identify first blocks as IV in the encrypted string would be enough for me to switch to use old IV? I think AesCryptoServiceProvider saves the IV before the encrypted string.
kelalaka avatar
in flag
@BharatMalhotra If you expect that it is plaintext, then make sure that the byte are less than 128? The usual practice is saving together. I couldn't see it from the documentation. You can test it yourself very easily. The size of the ciphertext can indicate this. Encrypt one letter, if the ciphertext is 16-byte then there is no saving the IV,,,,
Bharat Malhotra avatar
us flag
Thanks, I think you are right. It does not save IV with the encrypted string. Any links to refer to see how we can do that so later i can detect that one for new data?
kelalaka avatar
in flag
It is software practice, you can add a magic number as [Maarten](https://crypto.stackexchange.com/a/96527/18298) suggested showing that the IV is static or random. Note that when building software makes sure that you can differentiate the version of the data if needed. Updates should consider all cases...
Score:4
in flag

You cannot reliably check if the data has a random prefixed or not. For the same key, the data will even generally not fail unpadding. So what you need to do is to use a different protocol.

For instance, you can have a 16 byte fully random magic in front of the newly encrypted files (generated once, of course), then a version number and other data including a random IV, the ciphertext and a HMAC over the header and data including the IV. That way you can detect that the right encryption is used (as generating that magic by accident has got a probability of 1 in $2^{128}$) by simply doing a compare. You've also protected your file for integrity and authenticity using the HMAC (this is optional, and not strictly required for confidentiality - but it comes highly recommended).

So that would be:

 MAGIC (16 bytes) | VERSION | IV (16 bytes) | CIPHERTEXT | TAG

Where TAG is the authentication tag produced using HMAC over everything before.

Of course, there have been many people that have written protocols like these already as well, so you may want to look into container formats such as CMS (which usually depends on public key encryption / certificates) or even NaCL.

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.