copenssldesstack-corruption

Stack corruption upon calling OpenSSL DES_cbc_encrypt()


This is code to encrypt a struct using DES and then read it back after writing to File :

#include "openssl/des.h"
#include "openssl/conf.h"
#include "openssl/evp.h"
#include "openssl/err.h"
#include <stdio.h>


typedef struct MyStruct_t
{
        char m_Name[32];
        char m_ID[16];
} MyStruct;


uint8_t* __stdcall myStructToBytes(const MyStruct * const iMyStruct)
{
    const size_t len = sizeof(MyStruct;
    uint8_t *buffer = (uint8_t*) malloc(len);

    memcpy(buffer, iMyStruct, len);
    return buffer;
}

const int __stdcall encodeMyStructToFile(const char *sOutputFilename,
                                                         const char *sKey, const MyStruct *iStruct)
{
    DES_cblock key, iv;
    DES_key_schedule schedule;
    unsigned char *data = myStructToBytes((const MyStruct * const)iStruct);
    long len = sizeof(MyStruct);
    int ret = 0;

    memcpy(key, &sKey[0], 8);
    memcpy(iv, &sKey[0], 8);
    DES_set_odd_parity(&key);
    ret = DES_set_key_checked(&key, &schedule);

    if(ret == 0) {
        FILE *write_ptr;
        unsigned char *encrypted = (unsigned char *)malloc(len);
        DES_cbc_encrypt(data, encrypted, len, &schedule, &iv, DES_ENCRYPT);

        // Write encrypted data to file...
        fopen_s(&write_ptr, sOutputFilename, "wb");
        fwrite(encrypted, len, 1, write_ptr);
        fclose(write_ptr);
        free(encrypted);
    }
    free(data);

    return ret;
}


const int __stdcall decodeMyStructFromFile(const char *sInputFilename,
           const char *sKey, MyStruct *oStruct)
{
    int ret = 0;
    DES_cblock key, iv;
    DES_key_schedule schedule;
    FILE *read_ptr;
    long len = 0;
    unsigned char *encrypted = NULL;

    // Read encrypted data from file...
    fopen_s(&read_ptr, sInputFilename, "rb");
    ret = fseek(read_ptr, 0L, SEEK_END);
    len = ftell(read_ptr);
    rewind(read_ptr);
    encrypted = (unsigned char *)malloc(len);
    fread(encrypted, len, 1, read_ptr);
    fclose(read_ptr);

    // Perform decryption...
    memcpy(key, &sKey[0], 8);
    memcpy(iv, &sKey[0], 8);
    DES_set_odd_parity(&key);
    ret = DES_set_key_checked(&key, &schedule);

    if(ret == 0) {
        DES_cbc_encrypt(encrypted, (unsigned char *)oStruct, len, &schedule, &iv, DES_DECRYPT);
    }
    free(encrypted);

    return ret;
}

int main() {
    MyStruct tMyStruct;
    MyStruct tMyStruct1;
    char outputFilename[] = "abcd.bin";
    char key[] = "key_to_file";

    memset(&tMyStruct, 0, sizeof(MyStruct));
    memcpy_s(tMyStruct.m_Name, 256, "Hello", 5);
    memcpy_s(tMyStruct.m_ID, 32, "1234567890", 10);

    encodeMyStructToFile((const char *)outputFilename, (const char *)key, (const MyStruct * const)&tMyStruct);

    decodeLicenseStructFromFile(outputFilename, key, &tMyStruct1);


    return 0;
}

Somehow, I get stack corruption error in debugger for both encrypt and decrypt functions. I traced down the problem around DES_cbc_encrypt(). What am I doing wrong? I'm using compiled binaries for OpenSSL: openssl-1.1.0e-vs2012 on 64-bit.


Solution

  • The reason, why I came back here, is because I noticed (although not producible from the sample code), that both DES_cbc_encrypt() and DES_cbc_decrypt() functions expect 8-byte aligned inputs. Thus in the unfortunate case, if the struct here would have been not a multiple of 8 in size, DES_cbc_encrypt() would write past the end of buffer, thus the obvious stack-corruption. Although, when padding is not explicitly disabled, sizeof() still returns the sum of the sizes of the members, so this case I understand is the task of the application programmer. Maybe someone could light up more on this.