0. 头文件和辅助函数
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <cpuid.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <immintrin.h>
void printHex(unsigned char* c, int n)
{
int i;
for (i = 0; i < n; i++) {
printf("%02X", c[i]);
}
}
1. 使用/dev/random或/dev/urandom做熵源
/**
* 使用/dev/urandom使用非阻塞熵源
*/
int random_generator_dev(unsigned char* random, const uint64_t len)
{
if (random == NULL || len <= 0) return 1;
const char* file = "/dev/random";
int fd = open(file, O_RDONLY);
if (-1 == fd) return 2;
int nread = read(fd, random, len);
close(fd);
if (nread != len) return 3;
return 0;
}
2. 使用CPU硬件生成器
编译时需要添加 -mrdrnd
编译选项
提供内置接口和汇编指令两种实现方式。
#ifndef bit_RDRND
#define bit_RDRND 0x40000000
#endif
/**
* @brief 检测是否支持rdrand指令
*
* @return int 支持返回0, 不支持返回1.
*/
int cpu_supports_rdrand()
{
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
return !(ecx & bit_RDRND);
}
/**
* @brief 随机数生成
*
* @param rand_num
* @return int 0-有效,1-无效
*/
#if 0
int random_rdrand(uint64_t* rand_num)
{
uint64_t foo = 0;
int cf_error_status = 0;
__asm__ volatile(
"\n\
rdrand %%rax;\n\
mov $1,%%edx;\n\
cmovae %%rax,%%rdx;\n\
mov %%edx,%1;\n\
mov %%rax, %0;"
: "=r"(foo), "=r"(cf_error_status)::"%rax", "%rdx");
*rand_num = foo;
return !cf_error_status;
}
#else
int random_rdrand(uint64_t* rand_num)
{
if (!rand_num) return 1;
if (_rdrand64_step((unsigned long long*)rand_num)) return 0;
return 2;
}
#endif
int random_generator_rd(unsigned char* random, const uint64_t len)
{
if (random == NULL || len <= 0) return -1;
uint64_t type_len = sizeof(uint64_t);
uint64_t loop = len / type_len;
uint64_t left = len - loop * type_len;
uint64_t random_gen = 0;
uint64_t i;
for (i = 0; i < loop; i++) {
if (!random_rdrand(&random_gen))
memcpy(random + i * type_len, &random_gen, type_len);
}
if (left) {
if (!random_rdrand(&random_gen))
memcpy(random + type_len * loop, &random_gen, left);
else
return -1;
}
return 0;
}
3. 使用PCG软算法生成
// ----------------------------------------------
#define PCG32_INITIALIZER \
{ \
0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL \
}
typedef struct {
uint64_t state;
uint64_t inc;
} pcg32_random_t;
static pcg32_random_t pcg32_global = PCG32_INITIALIZER;
uint32_t pcg32_random_r(pcg32_random_t* rng);
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
rng->state = 0U;
rng->inc = (initseq << 1u) | 1u;
pcg32_random_r(rng);
rng->state += initstate;
pcg32_random_r(rng);
}
void pcg32_srandom(uint64_t seed, uint64_t seq)
{
pcg32_srandom_r(&pcg32_global, seed, seq);
}
uint32_t pcg32_random_r(pcg32_random_t* rng)
{
uint64_t oldstate = rng->state;
// Advance internal state
rng->state = oldstate * 6364136223846793005ULL + (rng->inc | 1);
// Calculate output function (XSH RR), uses old state for max ILP
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
uint32_t pcg32_random() { return pcg32_random_r(&pcg32_global); }
int random_rdpcg(uint32_t* random)
{
if (random == NULL) return 1;
*random = pcg32_random();
return 0;
}
int random_generator_pcg(unsigned char* random, const uint32_t len)
{
if (random == NULL || len <= 0) return -1;
uint32_t type_len = sizeof(uint32_t);
uint32_t loop = len / type_len;
uint32_t left = len - loop * type_len;
uint32_t random_gen = 0;
uint32_t i;
for (i = 0; i < loop; i++) {
if (!random_rdpcg(&random_gen))
memcpy(random + i * type_len, &random_gen, type_len);
}
if (left) {
if (!random_rdpcg(&random_gen))
memcpy(random + type_len * loop, &random_gen, left);
else
return -1;
}
return 0;
}
void pcg_test()
{
uint32_t random;
int rounds = 9;
pcg32_srandom(time(NULL) ^ (intptr_t)&printf, (intptr_t)&rounds);
for (size_t i = 0; i < 10; i++) {
random_rdpcg(&random);
printHex((unsigned char*)&random, 4);
printf("\n");
}
unsigned char random[1024] = {0};
int rounds = 9;
pcg32_srandom(time(NULL) ^ (intptr_t)&printf, (intptr_t)&rounds);
for (size_t i = 1; i < 57; i += 2) {
random_generator_pcg(random, i);
printHex(random, i);
printf("\n");
}
}
int main(int argc, char const* argv[])
{
pcg_test();
return 0;
}