cencryptionopensslevp-cipher

implementing openssl evp encryption


I am currently doing some testing on brute forcing some simple text using EVP in C.

The plaintext, ciphertext, key size, encryption method and methodology are provided. We simply need to try different key which is a known dictionary word.

THE MAIN ISSUE IS IF I TRY TO CALL THE EVP METHOD DIRECTLY FROM MAIN LIKE THIS:

int main(int argc, char const *argv[]) {
    evp((unsigned char *)"This is a top secret.");
    return 0;
}

It fails to find the key(cannot generate the same cipher)

But the call with clock(); called:

int main(int argc, char const *argv[])
{
    clock_t begin = clock();
    evp((unsigned char *)"This is a top secret.");
    return 0;
}

It works fine! Why is this happening? What am I doing wrong? Can someone explain what the problem is?

The encryption using aes-128-cbc to encrypt the plaintext, while the iv is 16 bytes NULL and the key is a single dictionary word with the length smaller than 16 and will be feed with empty space when its length is smaller than 16.

The Following code is used to perform the whole process:

void evp(unsigned char * plaintext) {
    FILE *fp = fopen("words.txt", "r");
    const char cipher[]  ="8d20e5056a8d24d0462ce74e4904c1b513e10d1df4a2ef2ad4540fae1ca0aaf9";
    unsigned char iv[16];
    unsigned char ciphertext[256];

    /**Init OpenSSL**/
    ERR_load_crypto_strings();
    OPENSSL_config(NULL);
    OpenSSL_add_all_algorithms();

    unsigned char word[17];
    while(read_word(fp, word)==0) {
        if(strlen((char *)word) < 16) {
            add_space(word, sizeof(word));
            encrypt(plaintext, strlen((char *)plaintext), word, iv, ciphertext);

            if(strcmp((char *)ciphertext, cipher) == 0 ) {
                printf("The key is : %s\n", word);
                fclose(fp);
                exit(0);
            };
        }
    }

    fclose(fp);
}

The add_space method simply feed the key with empty space:

int add_space(unsigned char *str, size_t len) {

    int eol_pos = strlen((char *)str);
    int empty_space = len - eol_pos - 1 ;

    //set space and eol
    memset(str+eol_pos, ' ', empty_space);
    memset(str+len, '\0', 0);

    return 0;
}

The read_word method read a single word from the dictionary:

int read_word(FILE *file, unsigned char * word) {
    //Using fscanf
    unsigned char word_buf[255];

    if(fscanf(file, "%s", word_buf) != EOF) {
        if( strlen( (char *)word_buf ) < 15 ) {
            strcpy((char *)word,(char *) word_buf);
        }
        return 0;
    } 
    return 1;
}

And the encrypt method is the main process to encrypt the plaintext:

void encrypt(unsigned char *plaintext, int plain_len ,unsigned char *key, unsigned char * iv, unsigned char * ciphertext) {
    EVP_CIPHER_CTX *ctx;
    int out_len;
    int ciphertext_len;
    char *buff;

    if(! (ctx = EVP_CIPHER_CTX_new()) ) {
        handleErrors();
    }

    if( EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1 )
        handleErrors();

    if( EVP_EncryptUpdate(ctx, ciphertext, &out_len, plaintext, plain_len ) != 1 ) {
        handleErrors();
    }
    ciphertext_len = out_len;

    if( EVP_EncryptFinal_ex(ctx, ciphertext+out_len, &out_len) != 1 ) {
        handleErrors();
    }
    ciphertext_len += out_len;

    EVP_CIPHER_CTX_cleanup(ctx);

    buff = malloc(ciphertext_len*2+1);
    memset(buff, 0, ciphertext_len*2+1);

    for (int i = 0; i < ciphertext_len; ++i)
    {
        char tmp[3];
        sprintf(tmp, "%02x", ciphertext[i]);
        strcat(buff, tmp);
    }

    strcpy((char *)ciphertext, buff);
    free(buff);
}

Solution

  • You're never actually setting iv to anything. Because iv is a local variable, its contents are uninitialized, and subsequently reading it results in undefined behavior.

    What's probably happening when you call clock is that the stack space that was used by this function probably left some null bytes somewhere on the stack that just so happens to be where iv will reside when the encrypt function is called. You can't depend on this behavior, however.

    Initialize iv to all zeros. Then you'll get the behavior you expect.

    unsigned char iv[16] = { 0 };