OpenSSL 中的 AES-CTR PRNG 实现具有一致的输出和内存泄漏问题

问题描述 投票:0回答:1

我正在通过 OpenSSL 使用 AES-CTR 模式为一个旨在安全数据擦除的项目实现伪随机数生成器 (PRNG) (nwipe)。我的目标是在多次运行中使用相同种子擦除磁盘时获得一致的随机数。然而,我在第四次运行时遇到了意外的行为,尽管输入参数相同,但随机数开始不同。此外,我正在努力解决我无法查明的潜在内存泄漏问题。

初始化调用:

int nwipe_aes_ctr_prng_init(NWIPE_PRNG_INIT_SIGNATURE) {
    nwipe_log(NWIPE_LOG_NOTICE, "Initialising AES CTR PRNG");

    if (*state == NULL) {
        *state = calloc(1, sizeof(aes_ctr_state_t)); // Using calloc for memory allocation and initialization
        if (*state == NULL) {
            nwipe_log(NWIPE_LOG_FATAL, "Failed to allocate memory for AES CTR PRNG state.");
            return -1; // Return an error code indicating a problem
        }
    }

    aes_ctr_prng_init((aes_ctr_state_t*)*state, (unsigned long*)(seed->s), seed->length / sizeof(unsigned long));

    return 0; // Success
}

随机数生成调用:

int nwipe_aes_ctr_prng_read(NWIPE_PRNG_READ_SIGNATURE) {
    u8* restrict bufpos = buffer;
    size_t words = count / SIZE_OF_AES_CTR_PRNG;

    for(size_t ii = 0; ii < words; ++ii) {
        aes_ctr_prng_genrand_uint128_to_buf((aes_ctr_state_t*) *state, bufpos);
        bufpos += SIZE_OF_AES_CTR_PRNG;  // Move to the next block
    }

    // Handle remaining bytes if count is not a multiple of SIZE_OF_AES_CTR_PRNG
    const size_t remain = count % SIZE_OF_AES_CTR_PRNG;
    if(remain > 0) {
        unsigned char temp_output[16];  // Temporary buffer for the last block
        aes_ctr_prng_genrand_uint128_to_buf((aes_ctr_state_t*) *state, temp_output);
        memcpy(bufpos, temp_output, remain);
    }

    return 0;  // Success
}

结构体定义:

typedef struct {
    EVP_CIPHER_CTX *ctx;
    unsigned char ivec[AES_BLOCK_SIZE];
    unsigned int num;
    unsigned char ecount[AES_BLOCK_SIZE];
} aes_ctr_state_t;

PRNG 本身。

随机数生成功能:

void aes_ctr_prng_genrand_uint128_to_buf(aes_ctr_state_t* state, unsigned char* bufpos) {
    int outlen;

    EVP_EncryptUpdate(state->ctx, bufpos, &outlen, bufpos, 16);
    // OpenSSL internally calls CRYPTO_ctr128_encrypt_ctr32
}

这是我实现的init函数。

初始化功能:

void aes_ctr_prng_init(aes_ctr_state_t* state, unsigned long init_key[], unsigned long key_length) {
    unsigned char key[32]; // Platz für einen 256-Bit Schlüssel

    memset(state->ivec, 0, AES_BLOCK_SIZE);
    state->num = 0;
    memset(state->ecount, 0, AES_BLOCK_SIZE);

    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, (unsigned char*)init_key, key_length * sizeof(unsigned long));
    SHA256_Final(key, &sha256); // Generiere den endgültigen Schlüssel

    state->ctx = EVP_CIPHER_CTX_new();
    if (state->ctx != NULL) {
        EVP_EncryptInit_ex(state->ctx, EVP_aes_256_ctr(), NULL, key, state->ivec);
    }
}

密钥初始化和随机生成实现: 使用前初始化会将 IV、计数器和

ecount
清零。密钥是在提供的种子上使用 SHA256 派生的,并初始化 AES-256-CTR 模式。随机数是通过就地加密缓冲区生成的。

遇到的问题: 尽管确保每次运行的种子和参数相同,但第四次迭代会产生不同的随机数。 Valgrind 报告基于 OpenSSL

CRYPTO_ctr128_encrypt_ctr32
函数中未初始化值的条件跳转,表明状态或缓冲区的初始化或使用方式存在潜在问题。

Valgrind 日志:

==40641== Conditional jump or move depends on uninitialised value(s)
==40641==    at 0x4B9F565: CRYPTO_ctr128_encrypt_ctr32 (ctr128.c:183)
==40641==    by 0x4C655A1: ossl_cipher_hw_generic_ctr (ciphercommon_hw.c:117)
==40641==    by 0x4C60FCC: ossl_cipher_generic_stream_update (ciphercommon.c:469)
==40641==    by 0x4B61076: EVP_EncryptUpdate (evp_enc.c:643)
==40641==    by 0x414314: aes_ctr_prng_genrand_uint128_to_buf (aes_ctr_prng.c:133)
==40641==    by 0x417F5F: nwipe_aes_ctr_prng_read (prng.c:293)
...

问题:

  1. 如何确保使用相同种子的多次运行中的随机数一致?
  2. 如何解决 Valgrind 报告的未初始化值?在初始化 AES-CTR PRNG 状态时是否缺少一个步骤?我无法像我一样找出初始化内存的方法有任何问题。但显然它会导致错误,总是在第三次运行后,第四次产生不同的数据。

任何调试这些问题的见解或建议将不胜感激。谢谢!

c encryption memory-leaks openssl aes
1个回答
0
投票

非常感谢大家的帮助。我能够解决这个问题。 导致该问题的原因可能是 bufpos 缓冲区溢出,从而损坏了其他参数的内存。 我现在修改了代码,提供了一个临时缓冲区,并写入了 4x 32 位而不是一次性 128 位,现在它可以正常运行了!

之前:

void aes_ctr_prng_genrand_uint128_to_buf(aes_ctr_state_t* state, unsigned char* bufpos) {
    int outlen;

    EVP_EncryptUpdate(state->ctx, bufpos, &outlen, bufpos, 16);
    // OpenSSL internally calls CRYPTO_ctr128_encrypt_ctr32
}

之后:

/* Generates pseudorandom numbers and writes them to a buffer.
   - state: Pointer to the initialized AES CTR PRNG state.
   - bufpos: Target buffer where the pseudorandom numbers will be written. */
void aes_ctr_prng_genrand_uint128_to_buf( aes_ctr_state_t* state, unsigned char* bufpos )
{
    unsigned char temp_buffer[16]; /* Intermediate buffer for 128 bits */
    int outlen;

    /* Generate pseudorandom numbers in the intermediate buffer */
    EVP_EncryptUpdate( state->ctx, temp_buffer, &outlen, temp_buffer, sizeof( temp_buffer ) );

    /* Write the data from the intermediate buffer to bufpos in four 32-bit steps.
       This process is crucial to prevent a buffer overflow of bufpos, as it ensures
       that exactly 16 bytes (128 bits) of pseudorandom data are safely transferred
       into bufpos. Copying the data in controlled 32-bit segments allows for precise
       management of the memory space allocated to bufpos, mitigating the risk of
       writing beyond its allocated size. */
    int i = 0;
    while( i < 4 )
    {
        /* Copy each 32-bit segment from the intermediate buffer to the target buffer.
           This step-by-step approach is essential for maintaining the integrity of
           the buffer and ensuring that only the intended amount of data is written.
           The ternary operator is used here for illustrative purposes and does not
           alter the functionality. */
        memcpy( bufpos + ( i * 4 ), temp_buffer + ( i * 4 ), 4 );
        i = ( i < 4 ) ? i + 1 : 4;  // Ternary operator to increment i or keep it at 4
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.