feat: init repo

This commit is contained in:
2023-11-29 17:39:18 +08:00
parent b0b3dfe3fc
commit ea5cc6cb59
3 changed files with 1098 additions and 0 deletions

843
ChaCha20_DRBG.c Executable file
View File

@@ -0,0 +1,843 @@
#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;
}

126
aesDrbg.py Executable file
View File

@@ -0,0 +1,126 @@
from Crypto.Cipher import AES
import secrets
import time
import binascii # 引入hexlify方法
class AES_CTR_DRBG:
def __init__(self, personalization_string: bytes = b"", nonce: bytes = b""):
self.keylen = 16 # AES密钥长度为128比特
self.blocklen = 16 # AES块大小为128比特
self.seedlen = 32 # 种子长度为256比特
self.outlen = 16 # 输出长度为128比特
# 重播种计数器,表明自初始化或者重播种期间获得新的熵输入依赖,请求随机数生成的次数
self.reseed_counter = 0
# 重播种计数器阈值,在重播种之前能够产生随机数的最大请求次数
# level 1 2^20次
# level 2 2^10次
self.reseed_interval_in_counter = 1<<30
# 重播种时间阈值,距离上一次重播种的最大时间间隔,单位 秒
# level 1 600s
# level 2 60s
self.reseed_interval_in_time = 6000
# 最小的熵输入长度
self.min_entropy_input_length = 32 # 256比特
# 最大的熵输入长度
self.max_ectropy_input_length = 1<<35 - 1 # 2^35比特
self.seed_material = ""
self.aes = AES.new(b"\x00" * self.keylen, AES.MODE_ECB)
self.AES_CTR_DRBG_Instantiate(personalization_string, nonce)
def AES_CTR_DRBG_Instantiate(self, personalization_string: bytes = b"", nonce: bytes = b""):
self.min_entropy = self.seedlen
self.entropy_input = secrets.token_bytes(self.min_entropy)
self.seed_material = self.entropy_input + nonce + personalization_string
self.seed_material = self.AES_CTR_DRBG_df(self.seed_material, self.seedlen)
self.Key = self.seed_material[:self.keylen]
self.V = self.seed_material[-self.blocklen:]
self.reseed_counter = 1
self.last_reseed_time = int(time.time())
def AES_CTR_DRBG_Update(self, seed_material, Key, V):
temp = b""
while len(temp) < len(seed_material):
self.V = (int.from_bytes(self.V, "big") + 1) % (1 << (8 * self.blocklen))
self.V = self.V.to_bytes(self.blocklen, "big")
temp += self.aes.encrypt(self.V)
temp = temp[:len(seed_material)]
temp = int.from_bytes(temp, "big") ^ int.from_bytes(seed_material, "big")
temp = temp.to_bytes(len(seed_material), "big")
self.Key = temp[:self.keylen]
self.V = temp[-self.blocklen:]
def AES_CTR_DRBG_df(self, input_string: bytes, number_of_bits_to_return: int):
L = len(input_string).to_bytes(4, "big")
N = number_of_bits_to_return.to_bytes(4, "big")
S = L + N + input_string + b"\x80"
while len(S) % self.blocklen != 0:
S += b"\x00"
temp = b""
i = 0
K = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"[:self.keylen]
while len(temp) < self.keylen + self.outlen:
IV = i.to_bytes(4, "big") + b"\x00" * (self.blocklen - 4)
temp += self.CBC_MAC(K, IV + S)
i += 1
K = temp[:self.keylen]
X = temp[self.keylen:self.keylen + self.outlen]
tmp = b""
while len(tmp) < number_of_bits_to_return:
X = self.aes.encrypt(X)
tmp += X
requested_bits = tmp[:number_of_bits_to_return]
return requested_bits
def CBC_MAC(self, Key, data_to_MAC):
chaining_value = b"\x00" * self.blocklen
for i in range(0, len(data_to_MAC), self.blocklen):
block = data_to_MAC[i:i + self.blocklen]
input_block = int.from_bytes(chaining_value, "big") ^ int.from_bytes(block, "big")
chaining_value = self.aes.encrypt(input_block.to_bytes(self.blocklen, "big"))
return chaining_value
def AES_CTR_DRBG_Reseed(self, additional_input: bytes):
self.min_entropy = self.seedlen
self.entropy_input = secrets.token_bytes(self.min_entropy)
self.seed_material = self.entropy_input + additional_input
self.seed_material = self.AES_CTR_DRBG_df(self.seed_material, self.seedlen)
self.AES_CTR_DRBG_Update(self.seed_material, self.Key, self.V)
self.reseed_counter = 1
self.last_reseed_time = int(time.time())
def AES_CTR_DRBG_Generate(self, requested_number_of_bits, additional_input: bytes = b""):
length = requested_number_of_bits // 8
returned_bits = b""
if self.reseed_counter > (1 << 48) or int(time.time()) - self.last_reseed_time > 600:
self.AES_CTR_DRBG_Reseed(additional_input)
if additional_input != b"":
additional_input = self.AES_CTR_DRBG_df(additional_input, self.seedlen)
self.AES_CTR_DRBG_Update(additional_input, self.Key, self.V)
else:
additional_input = b"\x00" * self.seedlen
while len(returned_bits) < length:
self.V = (int.from_bytes(self.V, "big") + 1) % (1 << (8 * self.blocklen))
self.V = self.V.to_bytes(self.blocklen, "big")
output_block = self.aes.encrypt(self.V)
returned_bits += output_block
self.AES_CTR_DRBG_Update(additional_input, self.Key, self.V)
self.reseed_counter += 1
return returned_bits[:length]
if __name__ == "__main__":
bit_len = int(input("Enter the length of the bit string to be generated(bit):"))
num = int(input("Enter the number of the bit string to be generated:"))
file_name = input("Enter the name of the saved file:")
start_time = time.time() # 记录开始时间
aesDrbg = AES_CTR_DRBG()
with open(file_name,"w") as f: # 改为以文本方式写入
for i in range(num):
hex_output = binascii.hexlify(aesDrbg.AES_CTR_DRBG_Generate(bit_len)).decode() # 转为十六进制并解码为string
f.write(hex_output + '\n') # 在每次写入后换行
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time # 计算经过的时间
print(f"running time: {elapsed_time} ") # 打印出运行时间

129
sm4Drbg.py Executable file
View File

@@ -0,0 +1,129 @@
import secrets
import time
from gmssl.sm4 import CryptSM4,SM4_ENCRYPT
import binascii
class SM4_RNG:
def __init__(self,personalization_string :bytes = b"",nonce:bytes = b""):
self.keylen= 16
self.reseed_counter = 0
self.reseed_interval_in_counter = 1<<30
self.reseed_interval_in_time = 6000
self.min_entropy_input_length = 32
self.max_ectropy_input_length = 1<<35 - 1
self.seedlen = 32
self.outlen = 16
self.blocklen = 16
self.seed_material = ""
self.sm4 = CryptSM4()
self.SM4_RNG_Instantiate(personalization_string,nonce)
def SM4_RNG_Instantiate(self,personalization_string :bytes = b"",nonce:bytes = b""):
self.min_entropy = self.min_entropy_input_length
self.entropy_input = secrets.token_bytes(self.min_entropy)
self.seed_material = self.entropy_input + nonce + personalization_string
self.seed_material = self.SM4_df(self.seed_material,self.seedlen)
self.Key = b"\x00" * self.keylen
self.V = b"\x00" * self.blocklen
self.SM4_RNG_Update(self.seed_material,self.Key,self.V)
self.reseed_counter = 1
self.last_reseed_time = int(time.time())
def SM4_RNG_Update(self,seed_material,Key,V):
temp = b""
self.sm4.set_key(Key,SM4_ENCRYPT)
while(len(temp) < self.seedlen):
V = (int.from_bytes(V,"big") + 1) % (1<<self.blocklen)
self.output_block = self.sm4.crypt_ecb(V.to_bytes(self.blocklen,"big"))
temp = temp + self.output_block
temp = temp[:self.seedlen]
temp = int.from_bytes(temp,"big") ^ int.from_bytes(seed_material,"big")
temp = temp.to_bytes(self.seedlen,"big")
self.Key = temp[:self.keylen]
self.V = temp[-self.blocklen:]
def SM4_df(self,input_string:bytes,number_of_bits_to_return:int):
L = len(input_string)
N = number_of_bits_to_return
S = L.to_bytes(4,"big") + N.to_bytes(4,"big") + input_string + b"\x80"
while(len(S) % self.outlen != 0):
S = S + b"\x00"
temp = b""
i = 0
K = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F \
\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"[:self.keylen]
while len(temp)<self.keylen + self.outlen:
IV = i.to_bytes(4,"big") + b"\x00" * (self.outlen - 4)
temp = temp + self.CBC_MAC(K,(IV+S))
i = i + 1
K = temp[:self.keylen]
X = temp[self.keylen+1:self.keylen+self.outlen]
tmp = b""
self.sm4.set_key(K,SM4_ENCRYPT)
while len(tmp) < number_of_bits_to_return:
X = self.sm4.crypt_ecb(X)
tmp = tmp + X
requested_bits = tmp[:number_of_bits_to_return]
return requested_bits
def CBC_MAC(self,Key,data_to_MAC):
self.sm4.set_key(Key,SM4_ENCRYPT)
chaining_value = b"\x00" * self.outlen
n = len(data_to_MAC) / self.outlen
for i in range(int(n)):
input_block = int.from_bytes(chaining_value,"big") ^ int.from_bytes(data_to_MAC[i*self.outlen:(i+1)*self.outlen],"big")
chaining_value = self.sm4.crypt_ecb(input_block.to_bytes(self.outlen,"big"))
chaining_value = chaining_value[:self.outlen]
output_block = chaining_value
return output_block
def SM4_RNG_Reseed(self,additional_input:bytes):
self.min_entropy = self.min_entropy_input_length
self.entropy_input = secrets.token_bytes(self.min_entropy)
self.seed_material = self.entropy_input + additional_input
self.seed_material = self.SM4_df(self.seed_material,self.seedlen)
self.SM4_RNG_Update(self.seed_material,self.Key,self.V)
self.reseed_counter = 1
self.last_reseed_time = int(time.time())
def SM4_RNG_Generate(self,requested_number_of_bits,additional_input:bytes=b""):
length = int(requested_number_of_bits / 8)
returned_bits = b""
if self.reseed_counter > self.reseed_interval_in_counter or int(time.time()) - self.last_reseed_time > self.reseed_interval_in_time:
self.SM4_RNG_Reseed(additional_input)
if additional_input != b"":
additional_input = self.SM4_df(additional_input,self.seedlen)
self.SM4_RNG_Update(additional_input,self.Key,self.V)
else:
additional_input = b"\x00" * self.seedlen
self.sm4.set_key(self.Key,SM4_ENCRYPT)
while(len(returned_bits) < length):
self.V = int.from_bytes(self.V,"big") + 1 % (1<<self.blocklen)
self.V = self.V.to_bytes(self.blocklen,"big")
output_block =self.sm4.crypt_ecb(self.V)
returned_bits = returned_bits + output_block
self.SM4_RNG_Update(additional_input,self.Key,self.V)
self.reseed_counter += 1
return returned_bits[:length]
if __name__ == "__main__":
bit_len = int(input("Enter the length of the bit string to be generated(bit):"))
num = int(input("Enter the number of the bit string to be generated:"))
file_name = input("Enter the name of the saved file:")
start_time = time.time()
sm4Drbg = SM4_RNG()
with open(file_name,"w") as f:
for i in range(num):
hex_output = binascii.hexlify(sm4Drbg.SM4_RNG_Generate(bit_len)).decode()
f.write(hex_output + '\n')
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Program took {elapsed_time} seconds to run.")