nicksona
nicksona

Reputation: 326

AES-256 from OpenSSL produces a different ciphertext each time

Here is the code:

#include <QCoreApplication>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <iostream>

void en_de_crypt(int should_encrypt, FILE *ifp, FILE *ofp, unsigned char *ckey, unsigned char *ivec) {

    const unsigned BUFSIZE=4096;
    unsigned char *read_buf = (unsigned char*)malloc(BUFSIZE);
    unsigned char *cipher_buf;
    unsigned blocksize;
    int out_len;
    EVP_CIPHER_CTX ctx;

    for(int i=0; i<8;i++)
    {
        printf("%c\n",ckey[i]);
    }

     printf("\n\n");

    for(int i=0; i<8;i++)
    {
        printf("%c\n",ivec[i]);
    }

    EVP_CipherInit(&ctx, EVP_aes_256_cbc(), ckey, ivec, should_encrypt);
    blocksize = EVP_CIPHER_CTX_block_size(&ctx);
    cipher_buf = (unsigned char *)malloc(BUFSIZE + blocksize);

    while (1) {

        // Read in data in blocks until EOF. Update the ciphering with each read.

        int numRead = fread(read_buf, sizeof(unsigned char), BUFSIZE, ifp);
        EVP_CipherUpdate(&ctx, cipher_buf, &out_len, read_buf, numRead);
        fwrite(cipher_buf, sizeof(unsigned char), out_len, ofp);
        if (numRead < BUFSIZE) { // EOF
            break;
        }
    }

    // Now cipher the final block and write it out.

    EVP_CipherFinal(&ctx, cipher_buf, &out_len);
    fwrite(cipher_buf, sizeof(unsigned char), out_len, ofp);

    // Free memory

    free(cipher_buf);
    free(read_buf);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    std::string pathToFiles = "C:/Temp/testEnc/dqdokoleda.7z";
    unsigned char ckey[8];
    unsigned char ivec[8];

    ckey[0] = 'p';
    ckey[1] = 'a';
    ckey[2] = 's';
    ckey[3] = 's';
    ckey[4] = 'w';
    ckey[5] = 'o';
    ckey[6] = 'r';
    ckey[7] = 'd';

    ivec[0] = 'a';
    ivec[1] = 'c';
    ivec[2] = 'g';
    ivec[3] = 't';
    ivec[4] = 'i';
    ivec[5] = 'j';
    ivec[6] = 'o';
    ivec[7] = 'r';



    FILE *fIN, *fOUT;

    // First encrypt the file

    fIN = fopen((pathToFiles).c_str(), "rb"); //File to be encrypted; plain text
    fOUT = fopen((pathToFiles+".enc").c_str(), "wb"); //File to be written; cipher text

    en_de_crypt(1, fIN, fOUT, ckey, ivec);

    fclose(fIN);
    fclose(fOUT);

    //Decrypt file now

    fIN = fopen((pathToFiles+".enc").c_str(), "rb"); //File to be read; cipher text
    fOUT = fopen((pathToFiles+".decrypted").c_str(), "wb"); //File to be written; cipher text

    en_de_crypt(0, fIN, fOUT, ckey, ivec);

    fclose(fIN);
    fclose(fOUT);

    std::cout << "END" << std::endl;

    return a.exec();
}

The problem is when I comment the decrypting part, each time the .enc file have different content. Then if I try to decrypt respectively uncomment decryption and comment encryption part of the code the file is not being decrypted properly(7zip says it could not open it). If I do not comment any part of the code the encryption and decryption is done successfully.

I am using QT5.5.1 and between encryption and decryption of the file I am cleaning, running qmake, rebuilding and running in debug mode with MSVC 12.0 compiler(this does not matter but if the program works properly for you - try cleaning and building between encryption and decryption of one file to see it does not work like it should).

Upvotes: 3

Views: 1865

Answers (1)

Artjom B.
Artjom B.

Reputation: 61892

AES supports a block size of 128 bit and key sizes of 128, 192 and 256 bit. The IV must be of the same size as the block size in many modes and especially in CBC mode. The problem is that you use only an IV of 64 bit and a key of 64 bit. You need to provide a 16 byte IV (for all AES variants) and a 32 byte key (for AES-256).

My guess is that EVP_CipherInit will try to read the full IV and key and will therefore read into uninitialized memory which may have arbitrary data in it in each program execution.

Keep in mind that an IV must be randomly generated for each encryption in order to provide semantic security. If the IV is static and you use the same key then an attack may deduce that you encrypted the same message only by observing the ciphertexts. Randomizing the ciphertexts is therefore a security property. If you want to check if the encryption worked, then you need to try to decrypt it and compare the result with the original plaintext. You can store the IV at the beginning of the ciphertext file format.

It looks like you want to use a password as a encryption key. Don't confuse passwords with keys. Passwords have very little entropy whereas keys usually look like noise. If you want to use passwords, then you need to derive a key from a password. This is usually done with PBKDF2 (PKCS5_PBKDF2_HMAC with EVP_sha256 if you want AES-256 keys) with a random salt and many iterations (at least 60 thousand, can be a couple million). You can store the salt at the beginning of the ciphertext file format alongside of the IV.

Upvotes: 8

Related Questions