openssl: Re-initialising an EVP_CIPHER_CTX does not work as expected

With AES-128-CTR (and quite possibly other ciphers, but I didn’t test it), re-initialising the EVP_CIPHER_CTX does not give the results we would expect. Consider this reproducer:

#include <openssl/evp.h>
#include <stdio.h>

int main(void)
{
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    unsigned char out[4];
    unsigned char in[4] = {
        0x0f, 0x0e, 0x0d, 0x0c
    };
    unsigned char key1[16] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f
    };
    unsigned char iv[16] = {
        0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
        0x03, 0x02, 0x01, 0x00
    };
    int ret = 1, outl = 0;
    size_t i;

    if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key1, iv))
        goto err;

    if (!EVP_EncryptUpdate(ctx, out, &outl, in, sizeof(in)))
        goto err;

    if (outl != 4)
        goto err;
    outl = 0;

    printf("First data block is:\n");
    for (i = 0; i < sizeof(out); i++)
        printf("%02X ", out[i]);
    printf("\n");

    if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key1, iv))
        goto err;

    if (!EVP_EncryptUpdate(ctx, out, &outl, in, sizeof(in)))
        goto err;

    if (outl != 4)
        goto err;

    printf("Second data block is:\n");
    for (i = 0; i < sizeof(out); i++)
        printf("%02X ", out[i]);
    printf("\n");

    ret = 0;
 err:
    if (ret)
        printf("Error\n");
    else
        printf("Success\n");
    return ret;
}

When I run this code against 1.1.1 I get this output:

First data block is:
2F A7 F4 9E 
Second data block is:
2F A7 F4 9E 
Success

This is as expected. We have reset the key and IV and run the same encryption again. Therefore we would expect to see the same output. When I run this against master I get this:

First data block is:
2F A7 F4 9E 
Second data block is:
BB 42 56 E4 
Success

So the same encryption with the same key and IV gives different results in the re-initialised case.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (18 by maintainers)

Commits related to this issue

Most upvoted comments

Are there currently a set of tests that are run on every cipher provider? If so, there should be a test to prevent other providers from making this mistake. If not, it would be a good thing to add a set of tests that every provider needs to pass. (Commenting as reporter of #12377)