Main Content

Context initialized incorrectly for digest operation

Context used for digest operation is initialized for a different digest operation

Description

This defect occurs when you initialize an EVP_MD_CTX context object for a specific digest operation but use the context for a different operation.

For instance, you initialize the context for creating a message digest only.

ret = EVP_DigestInit(ctx, EVP_sha256())
However, you perform a final step for signing:
ret = EVP_SignFinal(&ctx, out, &out_len, pkey);
The error is shown only if the final step is not consistent with the initialization of the context. If the intermediate update steps are inconsistent, it does not trigger an error because the intermediate steps do not depend on the nature of the operation. For instance, EVP_DigestUpdate works identically to EVP_SignUpdate.

Risk

Mixing up different operations on the same context can lead to obscure code. It is difficult to determine at a glance whether the current object is used for message digest creation, signing, or verification. The mixup can also lead to a failure in the operation or unexpected message digest.

Fix

After you set up a context for a certain family of operations, use the context for only that family of operations. For instance, use these pairs of functions for initialization and final steps.

  • EVP_DigestInit : EVP_DigestFinal

  • EVP_DigestInit_ex : EVP_DigestFinal_ex

  • EVP_DigestSignInit : EVP_DigestSignFinal

If you want to reuse an existing context object for a different family of operations, reinitialize the context.

Examples

expand all

#include <openssl/evp.h>

#define fatal_error() exit(-1)

int ret;
unsigned char *out_buf16;
unsigned int out_len16;

void func(unsigned char *src, size_t len){
  EVP_MD_CTX* ctx = EVP_MD_CTX_create();

  ret = EVP_SignInit_ex(ctx, EVP_sha256(), NULL);
  if (ret != 1) fatal_error();

  ret = EVP_SignUpdate(ctx, src, len);
  if (ret != 1) fatal_error();

  ret = EVP_DigestSignFinal(ctx, out_buf16, (size_t*) out_len16);

  if (ret != 1) fatal_error();
}

In this example, the context object is initialized for signing only with EVP_SignInit but the final step attempts to create a signed digest with EVP_DigestSignFinal.

Correction — Use One Family of Operations

One possible correction is to use the context object for signing only. Change the final step to EVP_SignFinal in keeping with the initialization step.

#include <openssl/evp.h>

#define fatal_error() exit(-1)

int ret;
unsigned char *out_buf16;
unsigned int out_len16;

void corrected_cryptomdbadfunction(unsigned char *src, size_t len, EVP_PKEY* pkey){
  EVP_MD_CTX* ctx = EVP_MD_CTX_create();

  ret = EVP_SignInit_ex(ctx, EVP_sha256(), NULL); 
  if (ret != 1) fatal_error();

  ret = EVP_SignUpdate(ctx, src, len);
  if (ret != 1) fatal_error();

  ret = EVP_SignFinal(ctx, out_buf16, &out_len16, pkey); 
  if (ret != 1) fatal_error();
}

Result Information

Group: Cryptography
Language: C | C++
Default: Off
Command-Line Syntax: CRYPTO_MD_BAD_FUNCTION
Impact: Medium

Version History

Introduced in R2018a