Main Content

CWE Rule 338

Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)

Since R2023a

Description

Rule Description

The product uses a Pseudo-Random Number Generator (PRNG) in a security context, but the PRNG's algorithm is not cryptographically strong.

Polyspace Implementation

The rule checker checks for these issues:

  • Predictable block cipher initialization vector

  • Predictable cipher key

  • Vulnerable pseudo-random number generator

Examples

expand all

Issue

This issue occurs when you use a weak random number generator for the block cipher initialization vector.

Risk

If you use a weak random number generator for the initiation vector, your data is vulnerable to dictionary attacks.

Block ciphers break your data into blocks of fixed size. Block cipher modes such as CBC (Cipher Block Chaining) protect against dictionary attacks by XOR-ing each block with the encrypted output from the previous block. To protect the first block, these modes use a random initialization vector (IV). If you use a weak random number generator for your IV, your data becomes vulnerable to dictionary attacks.

Fix

Use a strong pseudo-random number generator (PRNG) for the initialization vector. For instance, use:

  • OS-level PRNG such as /dev/random on UNIX® or CryptGenRandom() on Windows®

  • Application-level PRNG such as Advanced Encryption Standard (AES) in Counter (CTR) mode, HMAC-SHA1, etc.

For a list of random number generators that are cryptographically weak, see Vulnerable pseudo-random number generator.

Example — Predictable Initialization Vector
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <stdlib.h>
#define SIZE16 16

int func(EVP_CIPHER_CTX *ctx, unsigned char *key){
    unsigned char iv[SIZE16];
    RAND_pseudo_bytes(iv, 16);  //Noncompliant
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1);  //Noncompliant
}

In this example, the function RAND_pseudo_bytes declared in openssl/rand.h produces the initialization vector. The byte sequences that RAND_pseudo_bytes generates are not necessarily unpredictable.

Correction — Use Strong Random Number Generator

Use a strong random number generator to produce the initialization vector. 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 SIZE16 16

int func(EVP_CIPHER_CTX *ctx, unsigned char *key){
    unsigned char iv[SIZE16];
    RAND_bytes(iv, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}
Issue

This issue occurs when you use a weak random number generator for the encryption or decryption key.

Risk

If you use a weak random number generator for the encryption or decryption key, an attacker can retrieve your key easily.

You use a key to encrypt and later decrypt your data. If a key is easily retrieved, data encrypted using that key is not secure.

Fix

Use a strong pseudo-random number generator (PRNG) for the key. For instance:

  • Use an OS-level PRNG such as /dev/random on UNIX or CryptGenRandom() on Windows

  • Use an application-level PRNG such as Advanced Encryption Standard (AES) in Counter (CTR) mode, HMAC-SHA1, etc.

For a list of random number generators that are cryptographically weak, see Vulnerable pseudo-random number generator.

Example — Predictable Cipher Key

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

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv){
    unsigned char key[SIZE16];
    RAND_pseudo_bytes(key, 16);  //Noncompliant
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1);   //Noncompliant
}

In this example, the function RAND_pseudo_bytes declared in openssl/rand.h produces the cipher key. However, the byte sequences that RAND_pseudo_bytes generates are not necessarily unpredictable.

Correction — Use Strong Random Number Generator

One possible correction is to 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 SIZE16 16

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv){
    unsigned char key[SIZE16];
    RAND_bytes(key, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}
Issue

This issue occurs when you use cryptographically weak pseudo-random number generator (PRNG) routines.

The list of cryptographically weak routines flagged by this checker include:

  • rand, random

  • drand48, lrand48, mrand48, erand48, nrand48, jrand48, and their _r equivalents such as drand48_r

  • RAND_pseudo_bytes

Risk

These cryptographically weak routines are predictable and must not be used for security purposes. When a predictable random value controls the execution flow, your program is vulnerable to malicious attacks.

Fix

Use more cryptographically sound random number generators, such as CryptGenRandom (Windows), OpenSSL/RAND_bytes(Linux/UNIX).

Example — Random Loop Numbers
#include <stdio.h>
#include <stdlib.h>

volatile int rd = 1;
int main(int argc, char *argv[])
{   
    int j, r, nloops;
    struct random_data buf;
    int i = 0;
    
    nloops = rand();  //Noncompliant
    
    for (j = 0; j < nloops; j++) {
        if (random_r(&buf, &i))  //Noncompliant
            exit(1);
        printf("random_r: %ld\n", (long)i);
    }
    return 0;
}

This example uses rand and random_r to generate random numbers. If you use these functions for security purposes, these PRNGs can be the source of malicious attacks.

Correction — Use Stronger PRNG

One possible correction is to replace the vulnerable PRNG with a stronger random number generator.

#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

volatile int rd = 1;
int main(int argc, char* argv[])
{   
    int j, r, nloops;
    unsigned char buf;
    unsigned int seed;
    int i = 0;
    
    if (argc != 3) 
    {
        fprintf(stderr, "Usage: %s <seed> <nloops>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    
    seed = atoi(argv[1]);
    nloops = atoi(argv[2]);
    
    for (j = 0; j < nloops; j++) {
        if (RAND_bytes(&buf, i) != 1)
            exit(1);
        printf("RAND_bytes: %u\n", (unsigned)buf);
    }
    return 0;
}

Check Information

Category: Random Number Issues

Version History

Introduced in R2023a