CWE Rule 372
Description
Rule Description
The product does not properly determine which state it is in, causing it to assume it is in state X when in fact it is in state Y, causing it to perform incorrect operations in a security-relevant manner.
Polyspace Implementation
The rule checker checks for these issues:
Context initialized incorrectly for cryptographic operation
Context initialized incorrectly for digest operation
Incompatible padding for RSA algorithm operation
Inconsistent cipher operations
Missing cipher data to process
Missing cipher final step
Missing data for encryption, decryption or signing operation
Missing parameters for key generation
Examples
Context initialized incorrectly for cryptographic operation
This issue occurs when you initialize an EVP_PKEY_CTX
object for a specific public key cryptography operation but use the object for a
different operation.
For instance, you initialize the context for encryption.
ret = EVP_PKEY_encrypt_init(ctx);
ret = EVP_PKEY_decrypt(ctx, out, &out_len, in, in_len);
The checker detects if the context object used in these functions has been initialized
by using the corresponding initialization functions:
EVP_PKEY_paramgen
, EVP_PKEY_keygen
,
EVP_PKEY_encrypt
, EVP_PKEY_verify
,
EVP_PKEY_verify_recover
,EVP_PKEY_decrypt
,
EVP_PKEY_sign
, EVP_PKEY_derive
,and
EVP_PKEY_derive_set_peer
.
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 encryption, decryption, signature, or another operation. The mixup can also lead to a failure in the operation or unexpected ciphertext.
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 usage of the EVP_PKEY_CTX
context object.
For encryption with
EVP_PKEY_encrypt
, initialize the context withEVP_PKEY_encrypt_init
.For signature verification with
EVP_PKEY_verify
, initialize the context withEVP_PKEY_verify_init
.For key generation with
EVP_PKEY_keygen
, initialize the context withEVP_PKEY_keygen_init
.
If you want to reuse an existing context object for a different family of operations, reinitialize the context.
#include <openssl/evp.h> #define fatal_error() exit(-1) int ret; unsigned char *out_buf10; size_t out_len10; int func(unsigned char *src, size_t len, EVP_PKEY_CTX *ctx){ if (ctx == NULL) fatal_error(); ret = EVP_PKEY_decrypt_init(ctx); if (ret <= 0) fatal_error(); return EVP_PKEY_encrypt(ctx, out_buf10, &out_len10, src, len); //Noncompliant }
In this example, the context is initialized for decryption but used for encryption.
One possible correction is to initialize the object for encryption.
#include <openssl/evp.h> #define fatal_error() exit(-1) int ret; unsigned char *out_buf10; size_t out_len10; int func(unsigned char *src, size_t len, EVP_PKEY_CTX *ctx){ if (ctx == NULL) fatal_error(); ret = EVP_PKEY_encrypt_init(ctx); if (ret <= 0) fatal_error(); return EVP_PKEY_encrypt(ctx, out_buf10, &out_len10, src, len); }
Context initialized incorrectly for digest operation
This issue 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())
ret = EVP_SignFinal(&ctx, out, &out_len, pkey);
EVP_DigestUpdate
works identically to
EVP_SignUpdate
.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.
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.
#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); //Noncompliant 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
.
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(); }
Incompatible padding for RSA algorithm operation
This issue occurs when you perform an RSA algorithm operation on a context object that is not compatible with the padding previously associated with the object.
For instance, you associate the OAEP padding scheme with a context object but later use the context for signature verification, an operation that the padding scheme does not support.
ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); ... ret = EVP_PKEY_verify(ctx, out, out_len, in, in_len);
Padding schemes remove determinism from the RSA algorithm and protect RSA operations from certain kinds of attack.
When you use an incorrect padding scheme, the RSA operation can fail or result in unexpected ciphertext.
Before performing an RSA operation, associate the context object with a padding scheme that is compatible with the operation.
Encryption: Use the OAEP padding scheme.
For instance, use the
EVP_PKEY_CTX_set_rsa_padding
function with the argumentRSA_PKCS1_OAEP_PADDING
or theRSA_padding_add_PKCS1_OAEP
function.You can also use the PKCS#1v1.5 or SSLv23 schemes. Be aware that these schemes are considered insecure.ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
You can then use functions such as
EVP_PKEY_encrypt
/EVP_PKEY_decrypt
orRSA_public_encrypt
/RSA_private_decrypt
on the context.Signature: Use the RSA-PSS padding scheme.
For instance, use the
EVP_PKEY_CTX_set_rsa_padding
function with the argumentRSA_PKCS1_PSS_PADDING
.You can also use the ANSI X9.31, PKCS#1v1.5, or SSLv23 schemes. Be aware that these schemes are considered insecure.ret = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING);
You can then use functions such as the
EVP_PKEY_sign
-EVP_PKEY_verify
pair or theRSA_private_encrypt
-RSA_public_decrypt
pair on the context.
If you perform two kinds of operation with the same context, after the first operation, reset the padding scheme in the context before the second operation.
#include <stddef.h> #include <openssl/rsa.h> #define fatal_error() exit(-1) int ret; unsigned char *out_buf; int func(unsigned char *src, size_t len, RSA* rsa){ if (rsa == NULL) fatal_error(); return RSA_private_encrypt(len, src, out_buf, rsa, RSA_PKCS1_OAEP_PADDING); //Noncompliant }
In this example, the function RSA_private_encrypt
performs a
signature operation by using the OAEP padding scheme, which supports encryption
operations only.
One possible correction is to use the RSA-PSS padding scheme. The corrected
example uses the function RSA_padding_add_PKCS1_PSS
to
associate the padding scheme with the context.
#include <stddef.h> #include <openssl/evp.h> #include <openssl/rsa.h> #define fatal_error() exit(-1) int ret; unsigned char *msg_pad; unsigned char *out_buf; int func(unsigned char *src, size_t len, RSA* rsa){ if (rsa == NULL) fatal_error(); ret = RSA_padding_add_PKCS1_PSS(rsa, msg_pad, src, EVP_sha256(), -2); if (ret <= 0) fatal_error(); return RSA_private_encrypt(len, msg_pad, out_buf, rsa, RSA_NO_PADDING); }
Inconsistent cipher operations
This issue occurs when you perform an encryption and decryption step with the same cipher context. You do not reinitialize the context in between those steps. The checker applies to symmetric encryption only.
For instance, you set up a cipher context for decryption using EVP_DecryptInit_ex
.
EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
EVP_EncryptUpdate
.EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
Mixing up encryption and decryption steps can lead to obscure code. It is difficult to determine at a glance whether the current cipher context is used for encryption or decryption. The mixup can also lead to race conditions, failed encryption, and unexpected ciphertext.
After you set up a cipher context for a certain family of operations, use the context for only that family of operations.
For instance, if you set up a cipher context for decryption
using EVP_DecryptInit_ex
, use the context afterward
for decryption only.
#include <openssl/evp.h> #include <stdlib.h> /* Using the cryptographic routines */ unsigned char *out_buf; int out_len; unsigned char g_key[16]; unsigned char g_iv[16]; void func(unsigned char* src, int len) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx); /* Cipher context set up for decryption*/ EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, g_key, g_iv); /* Update step for encryption */ EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len); //Noncompliant }
In this example, the cipher context ctx
is
set up for decryption using EVP_DecryptInit_ex
.
However, immediately afterward, the context is used for encryption
using EVP_EncryptUpdate
.
One possible correction is to change the setup step. If you
want to use the cipher context for encryption, set it up using EVP_EncryptInit_ex
.
#include <openssl/evp.h> #include <stdlib.h> unsigned char *out_buf; int out_len; unsigned char g_key[16]; unsigned char g_iv[16]; void func(unsigned char* src, int len) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx); /* Cipher context set up for encryption*/ EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, g_key, g_iv); /* Update step for encryption */ EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len); }
Missing cipher data to process
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);
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.
Perform encryption or decryption in this sequence:
Initialization of cipher context
Update steps
Final step
Cleanup of context
#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.
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); }
Missing cipher final step
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);
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.
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);
#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.
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); }
Missing data for encryption, decryption or signing operation
This issue occurs when the data provided for an encryption, decryption, signing, or authentication operation is NULL or the data length is zero.
For instance, you unintentionally provide a NULL value for in
or a
zero value for in_len
in this decryption
operation:
ret = EVP_PKEY_decrypt(ctx, out, &out_len, in, in_len);
md
or sig
, or a zero
value for md_len
or sig_len
in this verification
operation:ret = EVP_PKEY_verify(ctx, md, mdlen, sig, siglen);
With NULL data or zero length, the operation does not occur. The redundant operation often indicates a coding error.
Check the placement of the encryption, decryption, or signing operation. If the operation is intended to happen, make sure that the data provided is non-NULL. Set the data length to a nonzero value.
#include <openssl/evp.h> #define fatal_error() exit(-1) int ret; int func(EVP_PKEY_CTX * ctx){ if (ctx == NULL) fatal_error(); unsigned char* sig = (unsigned char*) "0123456789"; unsigned char* md = (unsigned char*) "0123456789"; ret = EVP_PKEY_verify_init(ctx); if (ret <= 0) fatal_error(); ret = EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()); if (ret <= 0) fatal_error(); return EVP_PKEY_verify(ctx, sig, 0, md, 0); //Noncompliant }
In this example, the data lengths (third and fifth arguments to
EVP_PKEY_verify
) are zero. The operation fails.
One possible correction is to use a nonzero length for the signature and the data believed to be signed.
#include <openssl/evp.h> #define fatal_error() exit(-1) int ret; int func(EVP_PKEY_CTX * ctx){ if (ctx == NULL) fatal_error(); unsigned char* sig = (unsigned char*) "0123456789"; unsigned char* md = (unsigned char*) "0123456789"; ret = EVP_PKEY_verify_init(ctx); if (ret <= 0) fatal_error(); ret = EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()); if (ret <= 0) fatal_error(); return EVP_PKEY_verify(ctx, sig, 10, md, 10); }
Missing parameters for key generation
This issue occurs when you perform a key generation step with a context object without first associating the object with required parameters.
For instance, you associate a EVP_PKEY_CTX
context object with an
empty EVP_PKEY
object params
before key generation
:
EVP_PKEY * params = EVP_PKEY_new(); ... EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new(params, NULL); ... EVP_PKEY_keygen(ctx, &pkey);
Without appropriate parameters, the key generation step does not occur. The redundant operation often indicates a coding error.
Check the placement of the key generation step. If the operation is intended, make sure that the parameters are set before key generation.
Certain algorithms use default parameters. For instance, if you specify the DSA
algorithm when creating the EVP_PKEY_CTX
object, a default key
length of 1024 bits is
used:
kctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
However, the default parameters can generate keys that are too weak for encryption. Weak parameters can trigger another defect. To change default parameters, use functions specific to the algorithm. For instance, to set parameters, you can use these functions:
Diffie-Hellman (DH): Use
EVP_PKEY_CTX_set_dh_paramgen_prime_len
andEVP_PKEY_CTX_set_dh_paramgen_generator
.Digital Signature Algorithm (DSA): Use
EVP_PKEY_CTX_set_dsa_paramgen_bits
.RSA: Use
EVP_PKEY_CTX_set_rsa_padding
,EVP_PKEY_CTX_set_rsa_pss_saltlen
,EVP_PKEY_CTX_set_rsa_rsa_keygen_bits
, andEVP_PKEY_CTX_set_rsa_keygen_pubexp
.Elliptic curve (EC): Use
EVP_PKEY_CTX_set_ec_paramgen_curve_nid
andEVP_PKEY_CTX_set_ec_param_enc
.
#include <openssl/evp.h> #define fatal_error() exit(-1) int ret; int func(EVP_PKEY *pkey){ EVP_PKEY * params = EVP_PKEY_new(); if (params == NULL) fatal_error(); EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new(params, NULL); if (ctx == NULL) fatal_error(); ret = EVP_PKEY_keygen_init(ctx); if (ret <= 0) fatal_error(); return EVP_PKEY_keygen(ctx, &pkey); //Noncompliant }
In this example, the context object ctx
is associated with an
empty parameter object params
. The context object does not have
the required parameters for key generation.
One possible correction is to specify an algorithm, such as RSA, during context creation. For stronger encryption, use 2048 bits for key length instead of the default 1024 bits.
#include <openssl/evp.h> #include <openssl/rsa.h> #define fatal_error() exit(-1) int ret; int func(EVP_PKEY *pkey){ EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if (ctx == NULL) fatal_error(); ret = EVP_PKEY_keygen_init(ctx); if (ret <= 0) fatal_error(); ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048); if (ret <= 0) fatal_error(); return EVP_PKEY_keygen(ctx, &pkey); }
Check Information
Category: State Issues |
Version History
Introduced in R2024a
See Also
External Websites
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)