我需要一个使用 CSPRNG(加密安全伪随机数生成器)的洗牌函数,并且可以手动播种以获得相同种子的相同输出。
内置的random.shuffle()
在Python中可以手动播种,但不适合加密用途并将在 python 3.11 版本中删除。
Crypto.Random.random.shuffle()
来自 PyCryptodome据我所知,不接受种子。
目前,我已经选择了内置的 Mersenne Twister。random.shuffle()
函数可以对数字列表进行洗牌,但这远非理想。随机排列 numpy 数组也很受欢迎,因为内置numpy.random.shuffle
不适合用于加密目的。
数组/列表可能包含超过 100 亿个元素,因此性能和随机性是关键。
创可贴代码如下。
import numpy as np
from random import seed, shuffle
array = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(array)
seed(1)
shuffle(array)
print(array) # [6 8 9 7 5 3 0 4 1 2]
CSPRNG 经常遇到重新播种等问题,在运行期间从操作系统内的熵池中提取熵。因此,最好使用流密码,例如计数器模式下的 AES。
那么,洗牌操作始终以相同的方式执行也很重要。类似地,生成的比特流中的数字应始终以相同的方式运行。如果这些被优化或以其他方式改变,结果将是不同的洗牌,破坏方案。
最后,您最好自己编程,这样您就可以确保方法契约背后的代码不会改变。
对此的要求是:
- 具有密钥大小的种子的流密码;
- 实现“拒绝采样”以获得一定范围内的随机数;
- Fisher-Yates 洗牌可创建完全随机的洗牌。
如果大小不符合流密码,则可以对种子进行散列并获取最左边的字节。
想法的演示(未经过充分测试或精心设计):
import numpy as np
from Crypto.Cipher import ChaCha20
from Crypto.Random import get_random_bytes
array = np.arange(100) # [0 1 2 3 4 5 6 7 8 9]
seed = bytes([0x00]) * 32 # use SHA-256 to hash different size seeds
nonce_rfc7539 = bytes([0x00]) * 12
cipher = ChaCha20.new(key=seed, nonce=nonce_rfc7539)
zerobuf = bytes([0x00]) * 5
def sample(max):
# rejection sampling using rand(0..n * max) % max
# the value 2 is in there to make sure the number of bits is at least
# two higher than max, so that the chance of each candicate succeeding
# is higher
stream_size = (max.bit_length() + 2 + 7) // 8
max_stream_value = 1 << (stream_size * 8)
max_candidate = max_stream_value - max_stream_value % max
while True:
stream = cipher.encrypt(zerobuf[0:stream_size])
candidate = int.from_bytes(stream, "big")
if (candidate < max_candidate):
break
return candidate % max
def shuffle(list):
# do the Fisher-Yates shuffle
for i in range(len(list) - 1, 0, -1):
j = sample(i + 1)
list[i],list[j] = list[j],list[i]
# test only
print(array)
for i in range(0, 100):
shuffle(array)
print(array)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)