该函数使用Windows "密码学API:下一代“(bcrypt.h)的S BCryptGenRandom函数生成随机字节--每个字符一个。
它返回写入缓冲区的字符数(不包括NUL-结束符)、参数错误时的-1或出错时的-2。
函数的
wchar_t str[9]; // 8 chars + NUL
rand_alphanum_str(str, sizeof (str));
printf("8 random alphanumeric characters: %wS\n", str);int rand_alphanum_str(WCHAR *buffer, size_t buffer_size) {
if(!buffer || buffer_size < 2) { return -1; }
int chars = buffer_size / sizeof (WCHAR) - 1; // -1 for null terminator
// need one random byte per character to be generated
unsigned char *rand_bytes = malloc(chars);
if(!rand_bytes) { return -2; }
// filling rand_bytes buffer
if(!NT_SUCCESS(BCryptGenRandom(NULL, rand_bytes, chars,
BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
free(rand_bytes);
return -2;
}
for(int i = 0; i < chars; ++i) {
// rand_bytes[i] is in range [0, 255].
// need a number in range [0, 61] - as there are 62 alphanumeric
// characters
// bit-twiddling to attempt to maintain a uniform distribution:
// discard 2 least-significant bits - rand_bytes is now in [0, 63]
rand_bytes[i] >>= 2;
if((rand_bytes[i] >> 5) & 1U) { // if the now most-significant bit is set,
rand_bytes[i] &= ~(1UL << 1); // clear the 2nd least-significant bit
// (only cleared if most-significant bit is set so as to avoid
// throwing distribution off by having a bit which is never set)
}
// rand_bytes[i] is now in [0, 61]
// of the 62 possible values of rand_bytes[i]:
// - [0, 9] represent numeric digits
// - [10, 35] represent uppercase letters
// - [36, 61] represent lowercase letters
// the offset of rand_bytes[i] from the low end of the range it lies in
// is added to said range's first ASCII value to give a random character
// in that range
if(rand_bytes[i] <= 9) {
buffer[i] = L'0' + rand_bytes[i];
} else if(rand_bytes[i] <= 35) {
// -10 for offset from beginning of numeral range
buffer[i] = L'A' + rand_bytes[i] - 10;
} else {
// -36 for offset from beginning of uppercase range
buffer[i] = L'a' + rand_bytes[i] - 36;
}
}
buffer[chars] = L'\0';
free(rand_bytes);
return chars;
}谢谢!
发布于 2020-07-26 20:10:26
输出字符串中的结果字符不是均匀分布的。实际上,一些字符从未出现在输出中。在将随机字节右移2之后,如果结果大于或等于32,则始终清除第二个最不重要位。因此,直到32的所有值都是可能的,但是它是32,33,36,37,40,41等等。而且,算法生成的每个值>= 32的概率是每个值< 32的两倍。
最好的解决方案是先将随机字节右移2,然后检查i是否小于62。如果是这样的话,就按原样使用。否则,生成一个新的随机字节并重复该进程。
您正在分配一个数组来将随机字节存储在其中。虽然这样做看起来很有效率,因为假设您当时只需要调用一个BCryptGenRandom(),但是如果您想要生成大的随机字符串,这可能是一个问题。此外,如果您希望有一个适当的均匀分布的输出,您可能需要生成比您最初预期的更多的随机字节。
考虑在堆栈上有一个包含随机字节的小数组,并使用这些数组生成字母数字字符。如果使用完所有字节,则使用一批新的随机字节填充数组。
发布于 2020-08-14 11:24:56
int rand_alphanum_str(WCHAR *buffer,size_t buffer_size) {
buffer_size保持不变,因此应该是一个常量。
if(!buffer || buffer_size < 2) { return -1; }我不知道为什么buffer_size 0和1的退化情况会导致错误。如果为零,则立即返回0。如果是1,则继续正常运行。
wchar_t str[9]; // 8 chars + NUL
...
int chars = buffer_size / sizeof (WCHAR) - 1; // -1 for null terminator抱歉,没有。这使得我们完全不清楚需要生成8个随机字符。程序员不需要提供9来生成8个字符。我要么显式地指定缓冲区需要比文档中的参数大一个字符才能保存NUL,要么使用一个以NUL结尾的可变字符串作为参数(很明显,在NUL之前使用不同的字符)。
// need one random byte per character to be generated为什么?我会使用一个字母表字符串作为输入(请记住,const),然后从该字母表中随机选择字符。
您可以选择单个字符,只需在[0, size)范围内请求一个随机索引,其中size是字母表的大小。大多数库都有相应的函数(您只需给出size,而不是范围)。然后在那个位置选择字符。这使你的随机字符串生成器更加灵活,当然-无偏。我同意另一个答案,即可能存在偏见。
如果您想让它执行得更好,那么您可以在范围alphabet_size ^ password_size中请求一个数字,然后使用alphabet_size作为基执行基转换。
https://codereview.stackexchange.com/questions/246038
复制相似问题