Appending IV to CipherText garbles first few bytes of plain text

861 views Asked by At

I am having some trouble decrypting a string correctly. What is infuriating about it is that it is only the first few bytes that are messed up, the remaining characters are correct.

So I had my test program working fine when I was just using a hard coded IV for encrypting and decrypting. The program takes a String, encrypts it (using AES), and then I get a hex representation of the encrypted binary. The problem arose when I tried to append the IV onto the end of the cipher text. Peculiarly, the length of the hex String did not increase after appending the IV. But, the decryption function seems to be obtaining the IV from the end of the cipher text, otherwise it wouldn't be able to decrypt any of it, right?

I have tried a bunch of different things, like creating a buffer of the exact size it needs to be, and adding the cipher text and IV using memcpy. Here is the code:

AES Encryption

int encryptAes(const char *plainText, char *cipherText, const char *key) {
    unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

    int plainTextLength = strlen(plainText);
    int cipherTextLength = 0;
    int blockLength = 0;
    static const int MAX_PADDING_LENGTH = 16;

    EVP_CIPHER_CTX encryptCtx;
    EVP_CIPHER_CTX_init(&encryptCtx);

    EVP_EncryptInit_ex(&encryptCtx, EVP_aes_256_cbc(), NULL, key, iv);

    if (!EVP_EncryptUpdate(&encryptCtx, cipherText, &blockLength, (unsigned char *) plainText, plainTextLength) ) {
        printf("Error in EVP_EncryptUpdate \n");
        return 1;
    }
    cipherTextLength += blockLength;

    if (!EVP_EncryptFinal_ex(&encryptCtx, cipherText + cipherTextLength, &blockLength)) {
        printf("Error in EVP_EncryptFinal_ex \n");
        return 1;
    }
    cipherTextLength += blockLength;

    // Append the IV
    memcpy(cipherText + cipherTextLength, iv, 16);

    EVP_CIPHER_CTX_cleanup(&encryptCtx);
    return cipherTextLength;
}

AES Decryption

int decryptAes(const char *cipherText, char *decipheredPlainText, const size_t cipherTextLength, const char *key) {
//    unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
    unsigned char iv[16];
    memcpy(iv, cipherText + cipherTextLength, 16);

    int plainTextLength = 0;
    int blockLength = 0;

    EVP_CIPHER_CTX decryptCtx;
    EVP_CIPHER_CTX_init(&decryptCtx);

    EVP_DecryptInit_ex(&decryptCtx, EVP_aes_256_cbc(), NULL, key, iv);

    if (!EVP_DecryptUpdate(&decryptCtx, decipheredPlainText, &blockLength, cipherText, cipherTextLength)) {
        printf("Error in EVP_DecryptUpdate\n");
        return 1;
    }

    plainTextLength += blockLength;

    if (!EVP_DecryptFinal_ex(&decryptCtx, decipheredPlainText + plainTextLength, &blockLength)) {
        printf("Error in EVP_DecryptFinal_ex\n");
        return 1;
    }

    plainTextLength += blockLength;
    decipheredPlainText[plainTextLength] = '\0';

    EVP_CIPHER_CTX_cleanup(&decryptCtx);
    return plainTextLength;
}

This produces the output:

Original Plain Text     [cipher cipher cipher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]
Hexadecimal is          [be1c1aaa5827be124023a96a3360da922c244acd845e8914d03cfac69d312948e10f8ef7a99a64acbc6996724315f6cb0bf441ba3b08ab25cae64389f6ded77b1579e847d3e18ca89e71a3c4ec5ca4e3089b7bc2e6bc9ef8d175406bf4b53005a91e285d117e5990176d85793bd75853]
Decrypted Plain Text    [�}kaw&d��~C�Rmfpher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]

If I remove the line memcpy(cipherText + cipherTextLength, iv, 16); from the encryption function, and uncomment the hardcoded IV at the beginning of the decryption function (and comment the following two lines), the output is correct:

Original Plain Text     [cipher cipher cipher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]
Hexadecimal is          [be1c1aaa5827be124023a96a3360da922c244acd845e8914d03cfac69d312948e10f8ef7a99a64acbc6996724315f6cb0bf441ba3b08ab25cae64389f6ded77b1579e847d3e18ca89e71a3c4ec5ca4e3089b7bc2e6bc9ef8d175406bf4b53005a91e285d117e5990176d85793bd75853]
Decrypted Plain Text    [cipher cipher cipher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]

Note!, the hexadecimal is the same in both cases.

Can anyone see where I'm going wrong here. I encountered something similar to this before, but can't seem to remember how I got around it. Obviously there is something afoot when trying to append the IV. There is ample space in cipherText to accommodate the addition of the IV.

Thanks in advance.

2

There are 2 answers

1
This isn't my real name On BEST ANSWER

What is the return value from the encryptAes() supposed to represent? It looks to me like it's supposed to represent the length in bytes of the data produced by the encryption process. Are you accounting for the fact that the data you need to deal with is sixteen bytes longer than that? If you're encrypting your data in one place, and copying it to another place to call decryptAes() on, you're probably forgetting to account for the extra sixteen bytes.

If this is the case, then what you're doing, effectively, is this:

  1. Call encryptAes(), passing it the text to encrypt, the output buffer, and the encryption key.
  2. In encryptAes(), you generate the IV, perform the encryption, writing the encrypted data to the output buffer, calculating the length as you go.
  3. Next, copy the IV onto the end of the encrypted data, without adding it to the cipherTextLength counter.
  4. Return from encryptAes(), returning cipherTextLength to the caller. Note, this is a value sixteen bytes shorter than your actual data.
  5. Using this returned value as your data length, copy your data to the location from which you will decrypt it.
  6. Call decryptAes(), passing it the text to decrypt, the output buffer, the size of the encrypted data, and the key.
  7. In decryptAes(), copy the sixteen random bytes of data that immediately follow the encrypted data, into the IV.
  8. Use this random data as your IV to run the decryption.

The is the likeliest explanation I can think of for why your IV is getting corrupted.

1
mepilk On

Decrypting a text with the same key but incorrect IV will actually corrupt the first block of the plaintext output and decrypt the rest correctly. So I would say that your IV is incorrect, even if the plaintext is correct after the first few bytes.