我正在通过 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)
...
问题:
任何调试这些问题的见解或建议将不胜感激。谢谢!
非常感谢大家的帮助。我能够解决这个问题。 导致该问题的原因可能是 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
}
}