openssl/openssl

With AES-CCM ciphers, initializing an EVP_CIPHER_CTX with key and nonce seperately results in an error on fetching tag

Chiusa

#23.302 aperta il 14 gen 2024

 (8 commenti) (0 reazioni) (0 assegnatari)C (11.262 fork)batch import
branch: 3.0branch: 3.1branch: 3.2branch: masterhelp wantedtriaged: bug

Metriche repository

Star
 (30.157 stelle)
Metriche merge PR
 (Metriche PR in attesa)

Descrizione

Here's a basic reproducer:

#include <stdbool.h>
#include <string.h>

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

void openssl_assert(char *op, bool ok) {
  if (!ok) {
    printf("%s failed:\n", op);
    ERR_print_errors_fp(stdout);
    exit(1);
  }
}

int main() {
  uint8_t key[16] = {0};
  uint8_t nonce[12] = {0};
  char *msg = "happy birthday to me";
  size_t tag_len = 16;

  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  openssl_assert("EVP_CIPHER_CTX_new", ctx != NULL);

  int res = EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, key, NULL);
  openssl_assert("EVP_EncryptInit_ex(cipher, key)", res == 1);

  res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(nonce), NULL);
  openssl_assert("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_IVLEN)", res == 1);

  res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL);
  openssl_assert("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG)", res == 1);

  res = EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce);
  openssl_assert("EVP_EncryptInit_ex(nonce)", res == 1);

  int zero = 0;
  res = EVP_CipherUpdate(ctx, NULL, &zero, NULL, strlen(msg));
  openssl_assert("EVP_CipherUpdate(data_len)", res == 1);

  size_t ciphertext_len = strlen(msg) + tag_len;
  uint8_t *ciphertext = calloc(1, ciphertext_len);
  int written = 0;
  res = EVP_CipherUpdate(ctx, ciphertext, &written, (const unsigned char *)msg,
                         strlen(msg));
  openssl_assert("EVP_CipherUpdate(msg)", res == 1);
  openssl_assert("EVP_CipherUpdate(msg)", written == strlen(msg));

  uint8_t final_block[1] = {0};
  res = EVP_CipherFinal(ctx, final_block, &written);
  openssl_assert("EVP_CipherFinal(msg)", res == 1);
  openssl_assert("EVP_CipherFinal(msg)", written == 0);

  res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len,
                            ciphertext + strlen(msg));
  openssl_assert("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_GET_TAG)", res == 1);
}

If you change EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce) to be EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce) (that is, to provide the key for a second time), then it will pass.

For all other ciphers, it works to pass key and nonce in separate calls to EVP_EncryptInit_ex, so I believe this is a bug. Further, the fact that this error happens on fetching the tag and provides no error stack makes it appear to be a bug.

Guida contributor