从/dev/urandom等获取随机字节不足,one-time pad密钥长度异常求助
一次性密码本密钥长度“缩水”问题排查与解决
核心原因
- 你生成的密钥是二进制字节流,其中大概率包含ASCII值为0的
'\0'字节,而C语言的字符串操作(如strlen()、printf("%s"))会把'\0'当作字符串结束符,导致你看到的“字符串长度”或文件内容被截断,但实际上getrandom()确实生成了足够的字节数。 - 你没有检查
getrandom()的返回值,如果调用失败(比如被信号中断),内存可能未被完全覆盖,结合calloc()初始化的0值,也会造成密钥“变短”的错觉。
解决方法
- 强制检查
getrandom()返回值并补全字节getrandom()可能因信号中断返回部分字节,需要循环调用直到获取足够长度的密钥。 - 将密钥视为二进制数据而非字符串
- 不要用
strlen()判断密钥长度,直接使用传入的length参数。 - 读写文件时用
fwrite()/fread()这类二进制操作函数,避免fputs()/fgets()等字符串函数截断数据。
- 不要用
- 改用
malloc()替代calloc()calloc()会将内存初始化为0,完全没必要,malloc()更高效,反正后续会被getrandom()覆盖。
修正后的代码
#include <sys/random.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define ERROR(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0) uint8_t *genkey(const size_t length) { uint8_t *key = malloc(length * sizeof(uint8_t)); if (!key) ERROR("could not allocate memory"); ssize_t bytes_read; size_t remaining = length; uint8_t *ptr = key; // 循环读取,确保获取足够字节 while (remaining > 0) { bytes_read = getrandom(ptr, remaining, 0); if (bytes_read < 0) { // 被信号中断时重试,其他情况直接报错 if (errno == EINTR) continue; ERROR("getrandom failed"); } remaining -= bytes_read; ptr += bytes_read; } return key; }
密钥读写示例
写入密钥到文件时,必须用二进制模式:
void write_key(const uint8_t *key, size_t length, const char *path) { FILE *fp = fopen(path, "wb"); if (!fp) ERROR("failed to open key file"); if (fwrite(key, 1, length, fp) != length) { ERROR("failed to write key"); } fclose(fp); }
读取密钥时同样用fread()读取指定长度,避免字符串截断问题。
内容的提问来源于stack exchange,提问作者nikbott
相关产品推荐
相关产品推荐

