首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >随机字母数字字符串生成器函数(C、Windows、bcrypt.h)

随机字母数字字符串生成器函数(C、Windows、bcrypt.h)
EN

Code Review用户
提问于 2020-07-26 14:20:30
回答 2查看 797关注 0票数 3

该函数使用Windows "密码学API:下一代“(bcrypt.h)的S BCryptGenRandom函数生成随机字节--每个字符一个。

它返回写入缓冲区的字符数(不包括NUL-结束符)、参数错误时的-1或出错时的-2

函数的

示例用法:

代码语言:javascript
复制
wchar_t str[9]; // 8 chars + NUL
rand_alphanum_str(str, sizeof (str));
printf("8 random alphanumeric characters: %wS\n", str);

函数代码:

代码语言:javascript
复制
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;
}

谢谢!

EN

回答 2

Code Review用户

发布于 2020-07-26 20:10:26

输出不是均匀分布的

输出字符串中的结果字符不是均匀分布的。实际上,一些字符从未出现在输出中。在将随机字节右移2之后,如果结果大于或等于32,则始终清除第二个最不重要位。因此,直到32的所有值都是可能的,但是它是32,33,36,37,40,41等等。而且,算法生成的每个值>= 32的概率是每个值< 32的两倍。

最好的解决方案是先将随机字节右移2,然后检查i是否小于62。如果是这样的话,就按原样使用。否则,生成一个新的随机字节并重复该进程。

避免分配随机字节数组

您正在分配一个数组来将随机字节存储在其中。虽然这样做看起来很有效率,因为假设您当时只需要调用一个BCryptGenRandom(),但是如果您想要生成大的随机字符串,这可能是一个问题。此外,如果您希望有一个适当的均匀分布的输出,您可能需要生成比您最初预期的更多的随机字节。

考虑在堆栈上有一个包含随机字节的小数组,并使用这些数组生成字母数字字符。如果使用完所有字节,则使用一批新的随机字节填充数组。

票数 4
EN

Code Review用户

发布于 2020-08-14 11:24:56

int rand_alphanum_str(WCHAR *buffer,size_t buffer_size) {

buffer_size保持不变,因此应该是一个常量。

代码语言:javascript
复制
if(!buffer || buffer_size < 2) { return -1; }

我不知道为什么buffer_size 0和1的退化情况会导致错误。如果为零,则立即返回0。如果是1,则继续正常运行。

代码语言:javascript
复制
wchar_t str[9]; // 8 chars + NUL
...
int chars = buffer_size / sizeof (WCHAR) - 1; // -1 for null terminator

抱歉,没有。这使得我们完全不清楚需要生成8个随机字符。程序员不需要提供9来生成8个字符。我要么显式地指定缓冲区需要比文档中的参数大一个字符才能保存NUL,要么使用一个以NUL结尾的可变字符串作为参数(很明显,在NUL之前使用不同的字符)。

代码语言:javascript
复制
// need one random byte per character to be generated

为什么?我会使用一个字母表字符串作为输入(请记住,const),然后从该字母表中随机选择字符。

您可以选择单个字符,只需在[0, size)范围内请求一个随机索引,其中size是字母表的大小。大多数库都有相应的函数(您只需给出size,而不是范围)。然后在那个位置选择字符。这使你的随机字符串生成器更加灵活,当然-无偏。我同意另一个答案,即可能存在偏见。

如果您想让它执行得更好,那么您可以在范围alphabet_size ^ password_size中请求一个数字,然后使用alphabet_size作为基执行基转换。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/246038

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档