CryptGenRandom不返回统一结果

时间:2018-06-21 01:52:00

标签: c winapi random cryptography

在Windows上,我在C语言中使用CryptGenRandom API(我认为这等效于Linux上的/dev/random/dev/urandom)。为了确认这一点,我在Windows上使用CryptGenRandom制作了随机文件,在Linux上从/dev/urandom读取了文件,然后使用ent分析了结果。

我曾经使用CryptGenRandom(最初来自here)生成随机文件的代码示例:

#include <windows.h>

static void
secure_entropy(void *buf, size_t len)
{
    HCRYPTPROV h = 0;
    DWORD type = PROV_RSA_FULL;
    DWORD flags = CRYPT_VERIFYCONTEXT | CRYPT_SILENT;
    if (!CryptAcquireContext(&h, 0, 0, type, flags) ||
        !CryptGenRandom(h, len, buf)) {
        printf("failed to gather entropy");
        abort();
    }
    CryptReleaseContext(h, 0);
}

void test4()
{
    size_t size = 1 << 20;

    FILE *tfile = fopen("random_file", "w");
    char *buf = malloc(size);
    secure_entropy(buf, size);
    fwrite(buf, 1, size, tfile);
    fclose(tfile);
    free(buf);
}

但是,ent向我展示了随机结果的算术平均值约为127.05,而不是127.5(在Linux上是)。我有信心这不是偶然的事情,因为我在不同的计算机上多次复制它,并且结果是一致的。为了进一步研究它,我编写了一个python脚本来分析每个数字的频率(从0到255)。

f = open("random_file", "rb")
a = f.read()
f.close()

tmp = [0 for _ in range(256)]

for x in a:
    tmp[int(x)] += 1

print(tmp)

结果看起来像这样:

[4101, 4026, 4027, 4074, 4200, 4021, 4121, 4066, 4035, 3972, 4127, 4010, 
3978, 8214, 4009, 4155, 4083, 4065, 4067, 4064, 3993, 4021, 4136, 4112, 4221, 
4172, 4134, 4117, 3972, 4127, 4175, 4110, 4125, 4181, 4092, 4157, 4122, 4024, 
4020, 4088, 3980, 4140, 4159, 4129, 4064, 4141, 4096, 4238, 4036, 4080, 4151, 
4115, 4086, 4156, 4111, 4106, 4086, 4058, 4179, 4193, 4144, 4206, 4180, 4028, 
4148, 4015, 3979, 4201, 4098, 4146, 4169, 4120, 4044, 4066, 4049, 4051, 4051, 
4122, 4048, 4139, 4125, 4052, 4224, 4091, 4084, 4040, 4183, 4134, 3948, 4132, 
3955, 4162, 4183, 4014, 4100, 4091, 4005, 4146, 4182, 4032, 4037, 3985, 4098, 
4078, 4147, 4060, 4085, 4215, 4039, 4187, 4207, 4161, 4086, 4159, 4018, 4073, 
4051, 4008, 4095, 4110, 4160, 4288, 4077, 4074, 4113, 4104, 4097, 4115, 4049, 
3963, 4083, 4111, 4066, 4084, 4107, 4035, 3977, 4078, 4035, 4008, 3993, 4080, 
4152, 4121, 4111, 4033, 4094, 4191, 4131, 3978, 4082, 4134, 4119, 4135, 4071, 
3993, 3888, 4137, 4188, 4110, 4078, 4186, 4188, 4074, 4196, 4110, 4069, 4135, 
4043, 4150, 4023, 4095, 4074, 4179, 4112, 4084, 4124, 4180, 4154, 3996, 4103, 
4199, 4137, 4155, 4039, 4077, 4159, 4167, 4171, 4115, 4025, 4218, 4046, 4008, 
4178, 3969, 4135, 4077, 4044, 4080, 4085, 4230, 4161, 4151, 4056, 4222, 4033, 
4020, 4187, 4034, 4175, 4167, 3962, 4102, 4054, 3978, 4111, 4001, 4028, 4103, 
4088, 4054, 4049, 4164, 4136, 4110, 4181, 3964, 4098, 4046, 3997, 4151, 4122, 
4272, 4067, 4112, 4037, 4083, 4072, 4106, 4105, 4104, 4166, 4090, 4071, 4080, 
4070, 4087, 4162, 4060, 4237, 4061, 4044, 4128, 4051, 4097]

很明显,第13个数字(第14个数字)的出现概率是所有其他数字的两倍,这也可以解释127.05的算术平均值。

我不确定这是CryptGenRandom的错误还是执行不正确,但是我已经在64位Windows 10和32位Windows 7计算机上对其进行了测试,并且结果一致。那么有人有什么想法或可以帮助进一步调查和确认吗?

1 个答案:

答案 0 :(得分:6)

  

FILE *tfile = fopen("random_file", "w");

您正在以文本模式打开文件,并且每个字符分别写入约4000次。包括'\r''\n'个字符。

每次编写'\n'时,程序都会插入一个额外的'\r',因此大约有8000个'\r'字符,其ASCII值为13

在Windows中,您应以二进制模式fopen("random_file", "wb")打开非文本文件的文件。