Windows BCryptGenRandom 真随机数生成器
基于Windows Cryptography API: Next Generation (CNG)
Windows CNG API
_bcrypt = ctypes.WinDLL("bcrypt")
BCryptGenRandom = _bcrypt.BCryptGenRandom
BCryptGenRandom.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_ulong, ctypes.c_ulong]
BCryptGenRandom.restype = ctypes.c_ulong
- 直接调用Windows底层的
bcrypt.dll - 明确定义C函数参数和返回类型
- 检查API调用状态码
基础字节生成 (_gen_bytes)
def _gen_bytes(self, n: int) -> bytes:if n <= 0:return b""buf = ctypes.create_string_buffer(n)status = BCryptGenRandom(None, buf, ctypes.c_ulong(n), ctypes.c_ulong(BCRYPT_USE_SYSTEM_PREFERRED_RNG))if status != 0:raise OSError(f"BCryptGenRandom failed with status 0x{status:08X}")return buf.raw
BCRYPT_USE_SYSTEM_PREFERRED_RNG系统选择最佳熵源- 零拷贝返回
- 将非零状态码视为错误
2.2 精确比特控制 (random_bits_bytes)
def random_bits_bytes(self, n_bits: int) -> bytes:if n_bits <= 0:return b""n_bytes = (n_bits + 7) // 8 # 向上取整计算字节数data = self._gen_bytes(n_bytes)excess_bits = n_bytes * 8 - n_bitsif excess_bits:as_int = int.from_bytes(data, "big")as_int &= (1 << (n_bytes * 8 - excess_bits)) - 1 # 掩码清除data = as_int.to_bytes(n_bytes, "big")return data
比特字符串生成 (random_bits_str)
def random_bits_str(self, n_bits: int) -> str:b = self.random_bits_bytes(n_bits)bitstr = bin(int.from_bytes(b, "big"))[2:].rjust(len(b) * 8, "0")return bitstr[:n_bits]
- 获取精确比特数的字节数据
- 转换为二进制字符串表示
- 填充前导零确保完整字节表示
- 截取精确的比特数
均匀分布整数生成 (randint)
def randint(self, a: int, b: int) -> int:if a > b:raise ValueError("")width = b - a + 1if width == 1:return abits = width.bit_length()bytes_needed = (bits + 7) // 8while True:rnd = int.from_bytes(self._gen_bytes(bytes_needed), "big")rnd &= (1 << bits) - 1 # 掩码到精确比特数if rnd < width:return a + rnd
拒绝采样算法:
- 范围计算:
width = b - a + 1确定可能值的数量 - 比特优化:仅生成必要的随机比特
- 无偏保证:拒绝超出范围的值,确保均匀分布
熵源质量
Windows CNG提供的熵源包括:
硬件 RNG (Intel RDRAND/RDSEED)直接来自硬件的物理过程。
系统中断时序: 鼠标移动、键盘敲击、磁盘读写等硬件中断发生的时间点
硬件性能计数器 (HPC): CPU 周期、指令计数器等
用户输入事件: 鼠标的移动轨迹、按键的时间间隔等
网络数据包时序: 网络数据包到达网卡的时间差
调用
rng = TrueRNG()
encryption_key = rng.random_bytes(32) # 256位AES密钥
# 随机密码盐
salt = rng.random_bytes(16)
# 会话ID
session_id = rng.random_bits_str(128)
# 随机端口号
port = rng.randint(1024, 65535)
# 随机选择
options = ['A', 'B', 'C', 'D']
random_choice = options[rng.randint(0, len(options)-1)]