mimajingsai_3/ChaCha20_DRBG.c
2023-11-29 17:39:18 +08:00

844 lines
27 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
// #include <openssl/evp.h>
#define SUCCESS 0
#define ERROR -1
// 为ChaCha20设置适当的长度。
#define KEYLEN 32 // ChaCha20的密钥长度字节
#define COUNTERLEN 4 // ChaCha20的计数器长度字节
#define NONCELEN 12 // ChaCha20的nonce长度字节
#define OUTLEN 64 // 输出长度是64字节512位
#define SEEDLEN 96 // KEYLEN + OUTLEN字节
// for Instantiate
#define MAX_PERSONALIZATION_STRING_LEN 800 // (字节)
#define min_entropy_input_length 32 // (字节)
#define max_entropy_input_length pow(2, 35) / 8 // (字节)
// for handles
#define MAX_STATES 100
#define INVALID_HANDLE -1
// for Get_entropy_input
#define reseed_interval_in_counter pow(2, 10) // (次)
#define reseed_interval_in_time 60 //(秒)
#define MAX_NUMBER_OF_BITS 1024 // (比特)
// for entropy
#define BLOCK_SIZE 64
#define HASH_SIZE 32
/*----------------熵源的相关定义与函数----------------*/
typedef struct
{
uint8_t data[BLOCK_SIZE];
uint32_t datalen;
uint64_t bitlen;
uint32_t state[8];
} SHA256_CTX;
void sha256_transform(SHA256_CTX *ctx, const uint8_t data[])
{
uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
{
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
}
for (; i < 64; ++i)
{
m[i] = m[i - 16] + m[i - 7] + (m[i - 15] >> 7) + (m[i - 15] << 25) ^ (m[i - 15] >> 18) + (m[i - 15] << 14) ^ (m[i - 15] >> 3) + (m[i - 15] << 13);
}
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
for (i = 0; i < 64; ++i)
{
t1 = h + (e >> 6) + ((e & f) ^ (~e & g)) + 0x428a2f98 + m[i];
t2 = (a >> 2) + ((a & b) ^ (a & c) ^ (b & c)) + 0x5a827999;
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
void sha256_init(SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
void sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len)
{
uint32_t i;
for (i = 0; i < len; ++i)
{
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == BLOCK_SIZE)
{
sha256_transform(ctx, ctx->data);
ctx->bitlen += BLOCK_SIZE * 8;
ctx->datalen = 0;
}
}
}
void sha256_final(SHA256_CTX *ctx, uint8_t hash[])
{
uint32_t i;
i = ctx->datalen;
// Pad whatever data is left in the buffer
if (ctx->datalen < 56)
{
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else
{
ctx->data[i++] = 0x80;
while (i < BLOCK_SIZE)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, BLOCK_SIZE);
}
// Append the total bit length of the input data
ctx->bitlen += ctx->datalen * 8;
ctx->data[BLOCK_SIZE - 1] = ctx->bitlen;
ctx->data[BLOCK_SIZE - 2] = ctx->bitlen >> 8;
ctx->data[BLOCK_SIZE - 3] = ctx->bitlen >> 16;
ctx->data[BLOCK_SIZE - 4] = ctx->bitlen >> 24;
ctx->data[BLOCK_SIZE - 5] = ctx->bitlen >> 32;
ctx->data[BLOCK_SIZE - 6] = ctx->bitlen >> 40;
ctx->data[BLOCK_SIZE - 7] = ctx->bitlen >> 48;
ctx->data[BLOCK_SIZE - 8] = ctx->bitlen >> 56;
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i)
{
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}
void get_entropy_source(uint8_t *entropy_source)
{
// Simulate an entropy source by generating random data
for (int i = 0; i < min_entropy_input_length; i++)
{
entropy_source[i] = rand() % 256;
}
}
void Get_entropy_input(const uint8_t *data, size_t len, uint8_t *entropy)
{
SHA256_CTX ctx;
uint8_t hash[HASH_SIZE];
sha256_init(&ctx);
sha256_update(&ctx, data, len);
sha256_final(&ctx, hash);
memcpy(entropy, hash, HASH_SIZE);
}
/*----------------------------------------------*/
/*--------------------卡方检验相关定义与函数---------------------------*/
#define RANDOM_BITS_NUMBER 100
#define RANDOM_BITS_SIZE 128
int test_randomness(const unsigned char *bits)
{
// Test the randomness of the given bits using the chi-squared test
int ones = 0, zeros = 0;
for (int i = 0; i < RANDOM_BITS_SIZE / 8; i++)
{
unsigned char byte = bits[i];
for (int j = 0; j < 8; j++)
{
if ((byte >> j) & 0x01)
{
ones++;
}
else
{
zeros++;
}
}
}
double expected_ones = RANDOM_BITS_SIZE / 2.0;
double expected_zeros = RANDOM_BITS_SIZE / 2.0;
double chi_squared = (ones - expected_ones) * (ones - expected_ones) / expected_ones +
(zeros - expected_zeros) * (zeros - expected_zeros) / expected_zeros;
return chi_squared > 3.84;
}
/*-------------------------------------------------------*/
/*----------------ChaCha20的相关定义与函数------------------*/
#define ROTL32(v, n) (((v) << (n)) | ((v) >> (32 - (n))))
#define U32TO8_LITTLE(p, v) \
{ \
(p)[0] = (uint8_t)((v)); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24); \
}
#define U8TO32_LITTLE(p) \
(((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
// ChaCha的轮函数
#define QUARTERROUND(x, a, b, c, d) \
x[a] += x[b]; \
x[d] = ROTL32(x[d] ^ x[a], 16); \
x[c] += x[d]; \
x[b] = ROTL32(x[b] ^ x[c], 12); \
x[a] += x[b]; \
x[d] = ROTL32(x[d] ^ x[a], 8); \
x[c] += x[d]; \
x[b] = ROTL32(x[b] ^ x[c], 7);
static const char sigma[] = "expand 32-byte k";
static const char tau[] = "expand 16-byte k";
void ChaCha20_block(uint32_t state[16], uint32_t output[16])
{
int i;
memcpy(output, state, sizeof(uint32_t) * 16);
// 20轮操作每轮包括4个quarterrounds所以共80个quarterrounds
for (i = 0; i < 10; ++i)
{
// 双轮
QUARTERROUND(output, 0, 4, 8, 12)
QUARTERROUND(output, 1, 5, 9, 13)
QUARTERROUND(output, 2, 6, 10, 14)
QUARTERROUND(output, 3, 7, 11, 15)
QUARTERROUND(output, 0, 5, 10, 15)
QUARTERROUND(output, 1, 6, 11, 12)
QUARTERROUND(output, 2, 7, 8, 13)
QUARTERROUND(output, 3, 4, 9, 14)
}
for (i = 0; i < 16; ++i)
{
output[i] += state[i];
}
}
void ChaCha20_setup(uint32_t state[16], const uint8_t key[32], const uint8_t nonce[12], uint8_t counter[4])
{
const char *constants = (key[16] != 0 || key[24] != 0) ? sigma : tau;
state[4] = U8TO32_LITTLE(key + 0);
state[5] = U8TO32_LITTLE(key + 4);
state[6] = U8TO32_LITTLE(key + 8);
state[7] = U8TO32_LITTLE(key + 12);
state[8] = U8TO32_LITTLE(key + 16);
state[9] = U8TO32_LITTLE(key + 20);
state[10] = U8TO32_LITTLE(key + 24);
state[11] = U8TO32_LITTLE(key + 28);
// 前四个变量包含常量
state[0] = U8TO32_LITTLE(constants + 0);
state[1] = U8TO32_LITTLE(constants + 4);
state[2] = U8TO32_LITTLE(constants + 8);
state[3] = U8TO32_LITTLE(constants + 12);
// 加入计数器
state[12] = U8TO32_LITTLE(counter);
// 后三个变量包含nonce
state[13] = U8TO32_LITTLE(nonce + 0);
state[14] = U8TO32_LITTLE(nonce + 4);
state[15] = U8TO32_LITTLE(nonce + 8);
}
void ChaCha20_encrypt(uint32_t state[16], const uint8_t *in, uint8_t *out, size_t length, bool increment_flag)
{
uint32_t keystream[16];
uint8_t *keystream_bytes = (uint8_t *)keystream; // 为XOR操作重新解释keystream
size_t remaining = length;
size_t i;
while (remaining > 0)
{
size_t bytes_to_use = remaining < 64 ? remaining : 64; // 每个block 64 bytes
ChaCha20_block(state, keystream);
for (i = 0; i < bytes_to_use; ++i)
{
out[i] = in[i] ^ keystream_bytes[i];
}
if (increment_flag)
{
state[12] += 1; // 增加计数器
if (state[12] == 0)
{
state[13] += 1; // 处理计数器溢出
}
}
remaining -= bytes_to_use;
in += bytes_to_use;
out += bytes_to_use;
}
}
/*----------------------------------------------------*/
// 定义ChaCha20_DRBG的内部状态
typedef struct
{
uint8_t Key[KEYLEN]; // 用于ChaCha20的密钥
uint8_t V[COUNTERLEN]; // 用于ChaCha20的计数器
int reseed_counter; // 重播种计数值
int reseed_time; // 上次重播种时间值(秒)
bool used; // 表示此状态是否已经被使用
} DRBG_State;
// 全局变量
int instantiation_nonce = 0;
uint8_t chacha_nonce[NONCELEN] = {0};
static DRBG_State drbg_states[MAX_STATES];
uint32_t chacha_state[16];
uint8_t entropy_source[min_entropy_input_length];
size_t array_length;
size_t additional_input_length;
// 主要函数
int ChaCha20_DRBG_Instantiate_function(uint8_t *personalization_string, char *state);
char *ChaCha20_DRBG_Reseed_function(int state_handle, uint8_t *additional_input);
void ChaCha20_DRBG_Update(const uint8_t *provided_data, uint8_t *V, uint8_t *Key);
char *ChaCha20_DRBG_Generate_function(int state_handle, int requested_no_of_bits, uint8_t *additional_input, uint8_t **returned_bits);
// 算法函数
void ChaCha20_DRBG_Instantiate_algorithm(uint8_t *entropy_input, int nonce, uint8_t *personalization_string, uint8_t *V, uint8_t *Key, int *reseed_counter, int *reseed_time);
void ChaCha20_DRBG_Reseed_algorithm(uint8_t *V, uint8_t *Key, int *reseed_counter, int *reseed_time, uint8_t *entropy_input, uint8_t *additional_input);
char *ChaCha20_DRBG_Generate_algorithm(uint8_t *V, uint8_t *Key, int *reseed_counter, int requested_number_of_bits, uint8_t *additional_input, uint8_t **returned_bits);
// 派生函数
uint8_t *ChaCha20_df(uint8_t *V, uint8_t *input_string, size_t no_of_bits_to_return);
// 初始化所有状态为空闲
void initialize_states()
{
for (int i = 0; i < MAX_STATES; i++)
{
drbg_states[i].used = false;
}
}
// 查找并返回一个空闲的状态句柄。如果没有可用的空间则返回INVALID_HANDLE
int Find_state_space()
{
for (int i = 0; i < MAX_STATES; i++)
{
if (!drbg_states[i].used)
{
drbg_states[i].used = true; // 标记为已使用
return i; // 返回句柄/索引
}
}
return INVALID_HANDLE; // 所有的状态都在使用中
}
void increment_nonce(uint8_t *nonce)
{
int i = 0;
for (i; i < NONCELEN; i++)
{
nonce[i]++;
if (nonce[i] != 0)
{ // 检查是否有溢出
break; // 如果没有溢出,则不需要进位
}
}
}
int ChaCha20_DRBG_Instantiate_function(uint8_t *personalization_string, char *status)
{
if (strlen((char *)personalization_string) > MAX_PERSONALIZATION_STRING_LEN)
{
strcpy(status, "Personalization_string too long");
return ERROR;
}
uint8_t entropy_input[min_entropy_input_length];
get_entropy_source(entropy_source);
Get_entropy_input(entropy_source, min_entropy_input_length, entropy_input);
instantiation_nonce += 1;
increment_nonce(chacha_nonce);
uint8_t V[COUNTERLEN], Key[KEYLEN];
int reseed_counter, reseed_time;
ChaCha20_DRBG_Instantiate_algorithm(entropy_input, instantiation_nonce, personalization_string, V, Key, &reseed_counter, &reseed_time);
int state_handle = Find_state_space();
if (state_handle == ERROR)
{
strcpy(status, "Failed to find state space");
return ERROR;
}
// Save the internal state
memcpy(drbg_states[state_handle].V, V, COUNTERLEN);
memcpy(drbg_states[state_handle].Key, Key, KEYLEN);
drbg_states[state_handle].reseed_counter = reseed_counter;
drbg_states[state_handle].reseed_time = reseed_time;
strcpy(status, "Success");
return state_handle;
}
char *ChaCha20_DRBG_Reseed_function(int state_handle, uint8_t *additional_input)
{
// Check for the validity of state_handle.
if (!drbg_states[state_handle].used)
{
return "State not available for the indicated state_handle";
}
// Get the internal state values.
uint8_t *V = drbg_states[state_handle].V;
uint8_t *Key = drbg_states[state_handle].Key;
// Check length of additional_input
if (additional_input_length > MAX_PERSONALIZATION_STRING_LEN)
{
return "additional_input too long";
}
uint8_t entropy_input[min_entropy_input_length];
get_entropy_source(entropy_source);
Get_entropy_input(entropy_source, min_entropy_input_length, entropy_input);
// Invoke the reseed algorithm.
int reseed_counter, reseed_time;
ChaCha20_DRBG_Reseed_algorithm(V, Key, &reseed_counter, &reseed_time, entropy_input, additional_input);
// Save the internal state.
memcpy(drbg_states[state_handle].V, V, OUTLEN);
memcpy(drbg_states[state_handle].Key, Key, KEYLEN);
drbg_states[state_handle].reseed_counter = reseed_counter;
drbg_states[state_handle].reseed_time = reseed_time;
return "Success";
}
void ChaCha20_DRBG_Update(const uint8_t *provided_data, uint8_t *Key, uint8_t *V)
{
uint8_t temp[SEEDLEN] = {0};
// uint32_t V_uint32;
int i;
// memcpy(&V_uint32, V, sizeof(uint32_t));
ChaCha20_setup(chacha_state, Key, chacha_nonce, V);
ChaCha20_encrypt(chacha_state, V, temp, SEEDLEN, true);
// XOR provided_data with the leftmost part of temp
for (i = 0; i < SEEDLEN; ++i)
{
temp[i] ^= provided_data[i];
}
// Update the key and V from temp
memcpy(Key, temp, KEYLEN);
memcpy(V, temp + KEYLEN, COUNTERLEN);
}
char *ChaCha20_DRBG_Generate_function(int state_handle, int requested_no_of_bits, uint8_t *additional_input, uint8_t **returned_bits)
{
uint8_t *V;
uint8_t *Key;
int reseed_counter, reseed_time;
char *status;
// Check the validity of state_handle
if (!drbg_states[state_handle].V || !drbg_states[state_handle].Key)
{
return "State not available for the indicated state_handle";
}
// Get the internal state
V = drbg_states[state_handle].V;
Key = drbg_states[state_handle].Key;
reseed_counter = drbg_states[state_handle].reseed_counter;
reseed_time = drbg_states[state_handle].reseed_time;
// Check the rest of the input parameters
if (requested_no_of_bits > 4000)
{
return "Too many bits requested";
}
if ((additional_input_length) > MAX_PERSONALIZATION_STRING_LEN)
{
return "additional_input too long";
}
time_t timep;
int now_time = time(&timep);
// Check for reseeding
if (reseed_counter > reseed_interval_in_counter || (now_time - reseed_time) > reseed_interval_in_time)
{
status = ChaCha20_DRBG_Reseed_function(state_handle, additional_input);
if (strcmp(status, "Success") == 0)
{
V = drbg_states[state_handle].V;
Key = drbg_states[state_handle].Key;
reseed_counter = drbg_states[state_handle].reseed_counter;
reseed_time = drbg_states[state_handle].reseed_time;
additional_input = NULL;
additional_input_length = 0;
}
else
{
return status;
}
}
// Execute the algorithm to generate pseudorandom bits
status = ChaCha20_DRBG_Generate_algorithm(V, Key, &reseed_counter, requested_no_of_bits, additional_input, returned_bits);
if (strcmp(status, "Success") != 0)
{
return "Error in CTR_ChaCha20_DRBG_Generate_algorithm"; // Safety check
}
memcpy(drbg_states[state_handle].V, V, OUTLEN);
memcpy(drbg_states[state_handle].Key, Key, KEYLEN);
drbg_states[state_handle].reseed_counter = reseed_counter;
drbg_states[state_handle].reseed_time = reseed_time;
return "Success";
}
void ChaCha20_DRBG_Instantiate_algorithm(uint8_t *entropy_input, int nonce, uint8_t *personalization_string, uint8_t *V, uint8_t *Key, int *reseed_counter, int *reseed_time)
{
size_t entropy_input_len = min_entropy_input_length;
size_t personalization_string_len = strlen((char *)personalization_string);
size_t seed_material_size = entropy_input_len + sizeof(nonce) + personalization_string_len;
uint8_t *seed_material = malloc(seed_material_size);
uint8_t *requested_bits;
time_t timep;
int i = 0;
memcpy(seed_material, entropy_input, entropy_input_len);
memcpy(seed_material + entropy_input_len, &nonce, sizeof(nonce));
memcpy(seed_material + entropy_input_len + sizeof(nonce), personalization_string, personalization_string_len);
array_length = seed_material_size;
requested_bits = ChaCha20_df(V, seed_material, SEEDLEN * 8);
memcpy(seed_material, requested_bits, SEEDLEN);
memset(Key, 0, KEYLEN);
memset(V, 0, COUNTERLEN);
ChaCha20_DRBG_Update(seed_material, Key, V);
*reseed_counter = 1;
*reseed_time = time(&timep);
}
void ChaCha20_DRBG_Reseed_algorithm(uint8_t *V, uint8_t *Key, int *reseed_counter, int *reseed_time, uint8_t *entropy_input, uint8_t *additional_input)
{
// 1. seed_material = entropy_input || additional_input.
size_t seed_material_size = min_entropy_input_length + additional_input_length;
uint8_t *seed_material = malloc(seed_material_size);
memcpy(seed_material, entropy_input, min_entropy_input_length);
memcpy(seed_material + min_entropy_input_length, additional_input, additional_input_length);
// 2. seed_material = Block_Cipher_df(seed_material, 256).
uint8_t *requested_bits;
array_length = seed_material_size;
requested_bits = ChaCha20_df(V, seed_material, SEEDLEN * 8);
memcpy(seed_material, requested_bits, SEEDLEN);
// 3. (Key, V) = CTR_ChaCha20_DRBG_Update(seed_material, Key, V).
ChaCha20_DRBG_Update(seed_material, Key, V);
// 4. reseed_counter = 1.
time_t timep;
*reseed_counter = 1;
*reseed_time = time(&timep);
}
char *ChaCha20_DRBG_Generate_algorithm(uint8_t *V, uint8_t *Key, int *reseed_counter, int requested_number_of_bits, uint8_t *additional_input, uint8_t **returned_bits)
{
if (additional_input != NULL)
{
array_length = additional_input_length;
additional_input = ChaCha20_df(V, additional_input, SEEDLEN * 8);
ChaCha20_DRBG_Update(additional_input, Key, V);
}
else
{
// Assuming additional_input is a byte array, we set it to 0
additional_input = (uint8_t *)malloc(SEEDLEN);
additional_input_length = SEEDLEN;
if (additional_input == NULL)
{
return "Memory allocation failed";
}
memset(additional_input, 0, SEEDLEN);
}
uint8_t temp[requested_number_of_bits / 8];
ChaCha20_encrypt(chacha_state, V, temp, requested_number_of_bits / 8, true);
*returned_bits = (uint8_t *)malloc(requested_number_of_bits / 8);
memcpy(*returned_bits, temp, requested_number_of_bits / 8);
ChaCha20_DRBG_Update(additional_input, Key, V);
(*reseed_counter)++;
return "Success";
}
uint8_t *ChaCha20_df(uint8_t *V, uint8_t *input_string, size_t no_of_bits_to_return)
{
if (no_of_bits_to_return > MAX_NUMBER_OF_BITS)
{
return NULL;
}
uint32_t L = array_length / 8;
uint32_t N = no_of_bits_to_return / 8;
int S_init_size = 4 + 4 + array_length + 1;
uint8_t *S = (uint8_t *)malloc(S_init_size);
// Prepend the string length and the requested length to the input_string
memcpy(S, &L, 4);
memcpy(S + 4, &N, 4);
memcpy(S + 8, input_string, array_length);
S[S_init_size - 1] = 0x80; // Padding
// Pad S with zeros, if necessary.
int S_actual_size = S_init_size; // Taken the 0x80 into account
while (S_actual_size % OUTLEN != 0)
{
S = realloc(S, S_actual_size + 1); // Increase size by one byte
S[S_actual_size++] = 0x00; // Add a zero at current end, and increase actual size by one
}
int temp_len = 0;
uint8_t *temp = (uint8_t *)malloc(1);
uint32_t i = 0;
uint8_t K[KEYLEN];
uint8_t source[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
memcpy(K, source, KEYLEN);
ChaCha20_setup(chacha_state, K, chacha_nonce, V);
while (temp_len < KEYLEN + OUTLEN)
{
uint8_t IV[OUTLEN] = {0};
IV[OUTLEN - 4] = i; // 32-bit integer representation of i padded with zeros to OUTLEN bits.
uint8_t data_to_be_encrypted[OUTLEN + S_actual_size];
memcpy(data_to_be_encrypted, IV, OUTLEN);
memcpy(data_to_be_encrypted + OUTLEN, S, S_actual_size);
uint8_t data_encrypted_result[OUTLEN + S_actual_size];
ChaCha20_encrypt(chacha_state, data_to_be_encrypted, data_encrypted_result, temp_len, true);
temp_len = (i + 1) * (OUTLEN + S_actual_size);
temp = (uint8_t *)realloc(temp, temp_len);
memcpy(temp + i * (OUTLEN + S_actual_size), data_encrypted_result, temp_len);
i++;
}
memcpy(K, temp, KEYLEN);
uint8_t X[OUTLEN] = {0};
memcpy(X, temp + KEYLEN, OUTLEN);
uint8_t encrypted_data[OUTLEN] = {0};
temp[0] = '\0'; // Clear temp
temp_len = 0;
ChaCha20_setup(chacha_state, K, chacha_nonce, V);
while (temp_len < no_of_bits_to_return / 8)
{
ChaCha20_encrypt(chacha_state, X, encrypted_data, OUTLEN, true);
memcpy(X, encrypted_data, OUTLEN);
temp = (uint8_t *)realloc(temp, temp_len + OUTLEN);
memcpy(temp + temp_len, encrypted_data, OUTLEN);
temp_len += OUTLEN;
}
uint8_t *requested_bits = (uint8_t *)malloc(no_of_bits_to_return / 8);
if (requested_bits == NULL)
{
return NULL;
}
memcpy(requested_bits, temp, no_of_bits_to_return / 8);
memset(temp, 0, sizeof(temp));
memset(K, 0, sizeof(K));
memset(X, 0, sizeof(X));
memset(S, 0, sizeof(S));
return requested_bits;
}
int main()
{
uint8_t personalization_string[] = "this is personalization_string";
char status[MAX_STATES] = {'\0'};
int state_handle;
uint8_t *returned_bits;
long int returned_bits_len, returned_bits_num;
bool pass;
srand(time(0));
// 初始化DRBG
state_handle = ChaCha20_DRBG_Instantiate_function(personalization_string, status);
// 需要生成的比特串长度
printf("Enter the length of the bit string to be generated(bit): ");
scanf("%d", &returned_bits_len);
// returned_bits_len = 128;
// 需要生成的比特串个数
printf("Enter the number of the bit string to be generated: ");
scanf("%d", &returned_bits_num);
// returned_bits_num = 1000;
// char *filename; filename 未分配内存
char filename[100]; // 假设文件名长度不超过 99 个字符
printf("Enter the name of the saved file: ");
scanf("%s", filename);
// 生成的比特串保存在文本文件中
FILE *file = fopen(filename, "w");
if (file == NULL)
{
printf("无法打开文件\n");
return 1;
}
clock_t start_time = clock();
for (int j = 1; j <= returned_bits_num; ++j)
{
do
{
int zero_count = 0;
int one_count = 0;
additional_input_length = 0;
ChaCha20_DRBG_Generate_function(state_handle, returned_bits_len, NULL, &returned_bits);
for (int i = 0; i < returned_bits_len / 8; i++)
{
int bits = returned_bits[i];
for (int j = 0; j < 8; j++)
{
if (bits & 1)
one_count++;
else
zero_count++;
bits >>= 1;
}
}
const float expected_count = returned_bits_len / 2;
float chisq = (pow(zero_count - expected_count, 2) / expected_count) +
(pow(one_count - expected_count, 2) / expected_count);
if (chisq < 3.841)
pass = true;
else
pass = false;
} while (!pass);
// 将二进制数据转为十六进制后写入文件
char hex_str[returned_bits_len / 8 * 2 + 1];
for (int i = 0; i < returned_bits_len / 8; ++i)
{
sprintf(&hex_str[i * 2], "%02x", returned_bits[i]);
}
hex_str[returned_bits_len / 8 * 2] = '\0';
fprintf(file, "%s\n", hex_str); // 写入带有换行符的十六进制字符串
}
clock_t end_time = clock();
double execution_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
printf("running time: %f s\n", execution_time);
fclose(file);
return 0;
}