Main Content

CWE Rule 311

Missing Encryption of Sensitive Data

Since R2023b

Description

Rule Description

The software does not encrypt sensitive or critical information before storage or transmission.

Polyspace Implementation

The rule checker checks for these issues:

  • Missing cipher algorithm

  • Missing cipher data to process

  • Missing cipher final step

  • Missing cipher key

Examples

expand all

Issue

This issue occurs when you do not assign a cipher algorithm when setting up your cipher context.

You can initialize your cipher context without an algorithm. However, before you encrypt or decrypt your data, you must associate the cipher context with a cipher algorithm.

Risk

A missing cipher algorithm can lead to run-time errors or at least, non-secure ciphertext.

Before encryption or decryption, you set up a cipher context that has the information required for encryption: the cipher algorithm and mode, an encryption or decryption key and an initialization vector (for modes that require initialization vectors).

ret = EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), key, iv)
The function EVP_aes_128_cbc() specifies that the Advanced Encryption Standard (AES) algorithm must be used for encryption. The function also specifies a block size of 128 bits and the Cipher Bloch Chaining (CBC) mode.

Instead of specifying the algorithm, you can use NULL in the initialization step. However, before using the cipher context for encryption or decryption, you must perform an additional initialization that associates an algorithm with the context. Otherwise, the update steps for encryption or decryption can lead to run-time errors.

Fix

Before your encryption or decryption steps

ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len)
associate your cipher context ctx with an algorithm.
ret = EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv)

Example — Algorithm Missing During Context Initialization

#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char key[SIZE16];
unsigned char iv[SIZE16];
void func(void) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();       
    EVP_CIPHER_CTX_init(ctx);
    EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); //Noncompliant
}

In this example, an algorithm is not provided when the cipher context ctx is initialized.

Before you encrypt or decrypt your data, you have to provide a cipher algorithm. If you perform a second initialization to provide the algorithm, the cipher context is completely re-initialized. Therefore, the current initialization statement using EVP_EncryptInit_ex is redundant.

Correction — Provide Algorithm During Initialization

One possible correction is to provide an algorithm when you initialize the cipher context. In the corrected code below, the routine EVP_aes_128_cbc invokes the Advanced Encryption Standard (AES) algorithm. The routine also specifies a block size of 128 bits and the Cipher Block Chaining (CBC) mode for encryption.


#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char key[SIZE16];
unsigned char iv[SIZE16];
void func(unsigned char *src, int len, unsigned char *out_buf, int out_len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();       
    EVP_CIPHER_CTX_init(ctx);
    
    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); //Compliant
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
}
Issue

This issue occurs when you perform the final step of a block cipher encryption or decryption incorrectly.

For instance, you do one of the following:

  • You do not perform update steps for encrypting or decrypting the data before performing a final step.

    /* Initialization of cipher context */
    ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    ...
    /* Missing update step */
    ...
    /* Final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
  • You perform consecutive final steps without intermediate initialization and update steps.

    /* Initialization of cipher context */
    ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    ...
    /* Update step(s) */
    ret = EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
    ...
    /* Final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
    ...
    /* Missing initialization and update */
    ...
    /* Second final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
  • You perform a cleanup of the cipher context and then perform a final step.

    /* Initialization of cipher context */
    ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    ...
    /* Update step(s) */
    ret = EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
    ...
    /* Cleanup of cipher context */
    EVP_CIPHER_CTX_cleanup(ctx);
    ...
    /* Second final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);

Risk

Block ciphers break your data into blocks of fixed size. During encryption or decryption, the update step encrypts or decrypts your data in blocks. Any leftover data is encrypted or decrypted by the final step. The final step adds padding to the leftover data so that it occupies one block, and then encrypts or decrypts the padded data.

If you perform the final step before performing the update steps, or perform the final step when there is no data to process, the behavior is undefined. You can also encounter run-time errors.

Fix

Perform encryption or decryption in this sequence:

  • Initialization of cipher context

  • Update steps

  • Final step

  • Cleanup of context

Example — Missing Update Steps for Encryption Before Final Step

#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(void) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Missing update steps for encryption */
    
    /* Final encryption step */
    EVP_EncryptFinal_ex(ctx, out_buf, &out_len); //Noncompliant          
}

In this example, after the cipher context is initialized, there are no update steps for encrypting the data. The update steps are supposed to encrypt one or more blocks of data, leaving the final step to encrypt data that is left over in a partial block. If you perform the final step without previous update steps, the behavior is undefined.

Correction — Perform Update Steps for Encryption Before Final Step

Perform update steps for encryption before the final step. In the corrected code below, the routine EVP_EncryptUpdate performs the update steps.


#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(unsigned char *src, int len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);   
    
    /* Final encryption step */
    EVP_EncryptFinal_ex(ctx, out_buf, &out_len); //Compliant          
}
Issue

This issue occurs when you do not perform a final step after your update steps for encrypting or decrypting data.

For instance, you do the following:

/* Initialization of cipher context */
ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
...
/* Update step */
ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len);
...
/* Missing final step */
...
/* Cleanup of cipher context */
EVP_CIPHER_CTX_cleanup(ctx);

Risk

Block ciphers break your data into blocks of fixed size. During encryption or decryption, the update step encrypts or decrypts your data in blocks. Any leftover data is encrypted or decrypted by the final step. The final step adds padding to the leftover data so that it occupies one block, and then encrypts or decrypts the padded data.

If you do not perform the final step, leftover data remaining in a partial block is not encrypted or decrypted. You can face incomplete or unexpected output.

Fix

After your update steps for encryption or decryption, perform a final step to encrypt or decrypt leftover data.

/* Initialization of cipher context */
ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
...
/* Update step(s) */
ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len);
...
/* Final step */
ret = EVP_EncryptFinal_ex(&ctx, out_buf, &out_len);
...
/* Cleanup of cipher context */
EVP_CIPHER_CTX_cleanup(ctx);
Example — Cleanup of Cipher Context Before Final Step

#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(unsigned char *src, int len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);   
    
    /* Missing final encryption step */
    
    /* Cleanup of cipher context */
    EVP_CIPHER_CTX_cleanup(ctx); //Noncompliant
}

In this example, the cipher context ctx is cleaned up before a final encryption step. The final step is supposed to encrypt leftover data. Without the final step, the encryption is incomplete.

Correction — Perform Final Encryption Step

After your update steps for encryption, perform a final encryption step to encrypt leftover data. In the corrected code below, the routine EVP_EncryptFinal_ex is used to perform this final step.


#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(unsigned char *src, int len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);   
    
    /* Final encryption step */
    EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
    
    /* Cleanup of cipher context */
    EVP_CIPHER_CTX_cleanup(ctx); //Compliant
}
Issue

This issue occurs when you encrypt or decrypt data using a NULL encryption or decryption key.

Note

You can initialize your cipher context with a NULL key. However, before you encrypt or decrypt your data, you must associate the cipher context with a non-NULL key.

Risk

Encryption or decryption with a NULL key can lead to run-time errors or at least, non-secure ciphertext.

Fix

Before your encryption or decryption steps

ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len)
associate your cipher context ctx with a non-NULL key.
ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)

Sometimes, you initialize your cipher context with a non-NULL key

ret = EVP_EncryptInit_ex(&ctx, cipher_algo_1, NULL, key, iv)
but change the cipher algorithm later. When you change the cipher algorithm, you use a NULL key.
ret = EVP_EncryptInit_ex(&ctx, cipher_algo_2, NULL, NULL, NULL)
The second statement reinitializes the cipher context completely but with a NULL key. To avoid this issue, every time you initialize a cipher context with an algorithm, associate it with a key.

Example — NULL Key Used for Encryption

#include <openssl/evp.h>
#include <stdlib.h>
#define fatal_error() abort()

unsigned char *out_buf;
int out_len;

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv, unsigned char *src, int len){
    if (iv == NULL)
        fatal_error();
    
    /* Fourth argument is cipher key */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, iv); 
    
    /* Update step with NULL key */
    return EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len); //Noncompliant
}

In this example, the cipher key associated with the context ctx is NULL. When you use this context to encrypt your data, you can encounter run-time errors.

Correction — Use Random Cipher Key

Use a strong random number generator to produce the cipher key. The corrected code here uses the function RAND_bytes declared in openssl/rand.h.


#include <openssl/evp.h>
#include <openssl/rand.h>
#include <stdlib.h>
#define fatal_error() abort()
#define SIZE16 16

unsigned char *out_buf;
int out_len;

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv, unsigned char *src, int len){
    if (iv == NULL)
        fatal_error();
    unsigned char key[SIZE16];
    RAND_bytes(key, 16);
    
    /* Fourth argument is cipher key */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); 
    
    /* Update step with non-NULL cipher key */
    return EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len); //Compliant
}

Check Information

Category: Others

Version History

Introduced in R2023b