我目前正在开发一个CLI密码生成器,我一直在努力想出在一组定义的字符之间随机化字符的方法。我知道srand(time(NULL))
方法,但据我所知,它有点不一致,而且生成随机密码不太安全。我也知道有一种方法使用libsodium库的C随机数字(根据本主题),但我不确定如何使用它。我应该在我的项目中安装所有依赖项吗?拥有如此庞大的图书馆是一个相对较小的项目。虽然我计划随着时间的推移扩展它,但我不知道拥有一个庞大的库而不使用它的大部分功能是否值得。最重要的是,除了随机字符之外,是否有特定的算法来生成密码?我是否也应该按照另一种算法对数组本身进行随机化以获得更好的一致性,比如Fisher Yates Shuffle?提前感谢!
有很多问题需要解决,包括:
- 需求是一个功能还是一个程序?
- 如果它是一个函数,它可以使用相同的随机数生成器(种子)作为它所使用的程序的其他部分,或者它的随机数应该独立于由同一程序创建的其他序列?
- 如何创建一个好的随机种子? 假设你想要一个函数,函数的接口应该是什么?
- 例如:
extern void gen_random_password(size_t length, char buffer[length]);
- 指定数组中可用的字节数。
- 因此,密码将比指定的长度少一个字符,以允许空终止符。
- 您的o/s中有哪些随机数生成器可用:
rand()
和srand()
将"无处不在">- POSIX
nrand48()
-无隐藏种子 arc4random()
- no seed allowed (BSD, macOS)random()
andsrandom()
(BSD, macOS)
- 密码中允许使用哪些字符?
我喜欢使用nrand48()
,因为它允许随机密码生成器独立于任何其他序列运行一系列随机数,因为它接受种子-一个由3个unsigned short
整数组成的数组-作为参数。
生成一个好的随机种子是很棘手的。我的代码可以配置为使用任何这些机制:
- 读取/dev/random的值
- 读取/dev/random的值
- arc4random()的值
- 混合clock_gettime()和getpid()和16位CRC的值
- gettimeofday()和getpid()与16位CRC混合后的值
- 混合时间()和getpid()和16位CRC的值
首选前两个-/dev/random
和/dev/urandom
之间可能有也可能没有显著差异。
一旦解决了这些重要但乏味的问题,生成随机密码的核心算法就非常简单了:
grpwd43.h
#ifndef JLSS_ID_GRPWD43_H
#define JLSS_ID_GRPWD43_H
#include <stddef.h>
extern void gen_random_passwd(size_t length, char buffer[length]);
#endif /* JLSS_ID_GRPWD43_H */
grpwd43.c
#include "grpwd43.h" /* SSC: Self-sufficiency check */
#include <assert.h>
#include "randseed.h"
#include "prng48.h"
/*
** Tweak this list of alphanumerics plus punctuation to suit.
** For example, list the alphabet twice (or more) to make letters more
** likely than numbers or punctuation.
*/
static const char password[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
;
enum { NUM_PASSWORD = sizeof(password) - 1 };
static int initialized = 0;
void gen_random_passwd(size_t length, char buffer[length])
{
assert(buffer != NULL && length != 0);
if (buffer == NULL || length == 0)
return;
if (initialized == 0)
{
unsigned short seed[3];
random_seed_bytes(sizeof(seed), seed);
prng48_seed(seed);
initialized = 1;
}
for (size_t i = 0; i < length - 1; i++)
{
buffer[i] = password[prng48_rand(0, NUM_PASSWORD - 1)];
}
buffer[length - 1] = ' ';
}
#ifdef TEST
#include <stdio.h>
int main(int argc, char **argv)
{
for (int i = 11; i < 31; i++)
{
char passwd[i];
gen_random_passwd(i, passwd);
printf("%d: %sn", i - 1, passwd);
}
return 0;
}
#endif /* TEST */
所需的其他源文件可以在GitHub上的src/so-7594-6155子目录下的SOQ (Stack Overflow Questions)存储库中找到或者在src/libsoq子目录下。