Python的SystemRandom/os.urandom是否总有足够的熵用于良好的加密?
我有一个密码生成器:
import random, string
def gen_pass():
foo = random.SystemRandom()
length = 64
chars = string.letters + string.digits
return ''.join(foo.choice(chars) for _ in xrange(length))
根据文档,SystemRandom
使用 os.urandom
,而 os.urandom
又是通过 /dev/urandom
来生成随机的加密位。在 Linux 系统中,你可以从 /dev/urandom
或 /dev/random
获取随机位,它们都依赖于内核能获取的随机性。你可以通过运行 tail /proc/sys/kernel/random/entropy_avail
来检查可用的随机性,这个命令会返回一个数字,比如:129。数字越高,表示可用的随机性越多。/dev/urandom
和 /dev/random
的区别在于,/dev/random
只有在 entropy_avail
足够高(比如至少 60)时才会输出随机位,而 /dev/urandom
则会一直输出随机位。文档中提到,/dev/urandom
适合用于加密,而 /dev/random
主要用于 SSL 证书等情况。
我的问题是,gen_pass
生成的密码是否总是能达到强加密的标准?如果我尽可能快地调用这个函数,是否会因为随机性池耗尽而导致生成的密码不再强大?
另一个问题是,为什么 /dev/urandom
总是能生成强加密位,而不在乎 entropy_avail
的值?
有可能 /dev/urandom
的设计是让它的输出速度受到你能猜测的随机性数量的限制,但这只是猜测,我找不到确切的答案。
另外,这是我第一次在 StackOverflow 上提问,请给我一些反馈。我担心我提供了太多背景信息,而那些知道答案的人可能已经了解这些背景。
谢谢
更新
我写了一些代码来观察在读取 /dev/urandom
时随机性池的情况:
import subprocess
import time
from pygooglechart import Chart
from pygooglechart import SimpleLineChart
from pygooglechart import Axis
def check_entropy():
arg = ['cat', '/proc/sys/kernel/random/entropy_avail']
ps = subprocess.Popen(arg,stdout=subprocess.PIPE)
return int(ps.communicate()[0])
def run(number_of_tests,resolution,entropy = []):
i = 0
while i < number_of_tests:
time.sleep(resolution)
entropy += [check_entropy()]
i += 1
graph(entropy,int(number_of_tests*resolution))
def graph(entropy,rng):
max_y = 200
chart = SimpleLineChart(600, 375, y_range=[0, max_y])
chart.add_data(entropy)
chart.set_colours(['0000FF'])
left_axis = range(0, max_y + 1, 32)
left_axis[0] = 'entropy'
chart.set_axis_labels(Axis.LEFT, left_axis)
chart.set_axis_labels(Axis.BOTTOM,['time in second']+get_x_axis(rng))
chart.download('line-stripes.png')
def get_x_axis(rng):
global modnum
if len(filter(lambda x:x%modnum == 0,range(rng + 1)[1:])) > 10:
modnum += 1
return get_x_axis(rng)
return filter(lambda x:x%modnum == 0,range(rng + 1)[1:])
modnum = 1
run(500,.1)
如果运行这个代码,同时再运行:
while 1 > 0:
gen_pass()
那么我几乎可以可靠地得到一个这样的图表:
在运行 cat /dev/urandom
时生成的图表看起来相似,而 cat /dev/random
很快就会降到零并保持在低位(这个命令大约每 3 秒只读取一个字节)
更新
如果我运行相同的测试,但用六个实例的 gen_pass()
,我得到的是:
所以看起来有某种机制确保我有足够的随机性。我应该测量一下密码生成的速度,确保它确实被限制住了,因为如果没有限制,那可能就有问题了。
更新
我找到了一封 邮件链
这封邮件说,urandom
一旦随机性池只有 128 位时就会停止提取随机性。这与之前的结果非常一致,意味着在那些测试中,我生成的密码可能经常是无效的。
我之前的假设是,如果 entropy_avail
足够高(比如超过 64 位),那么 /dev/urandom
的输出就是好的。但现在看来,/dev/urandom
的设计是为了留出额外的随机性给 /dev/random
,以防它需要使用。
现在我需要找出一个 SystemRandom
调用需要多少真正的随机位。
3 个回答
你可能想看看这个链接,了解一下为什么使用 /dev/urandom 是个好选择:
/dev/random/
在读取时如果需要更多的随机性数据会让你等着。/dev/urandom/
则不会这样。所以,如果你用得太快,可能会导致随机性数据不够。虽然这样猜测出来的内容还是很难,但如果你真的很在意的话,可以选择从 /dev/random/
读取数据。理想情况下,应该使用一个非阻塞的读取循环,并加上一个进度指示器,这样你就可以移动鼠标来生成随机性数据,必要时用来补充。
/dev/random
和/dev/urandom
的输出有一点微妙的区别。正如之前提到的,/dev/urandom
不会阻塞,也就是说它不会让你等着。原因是它的输出来自一个伪随机数生成器,这个生成器是从/dev/random
里的“真实”随机数中获取种子的。
/dev/urandom
的输出几乎总是足够随机的——它是一个高质量的伪随机数生成器,使用了随机种子。如果你真的需要更好的随机数据源,可以考虑使用带有硬件随机数生成器的系统——比如我的上网本里就有一个VIA C7,它可以生成相当多的真正随机数据(我从/dev/random
里能稳定得到99.9kb/s,从/dev/urandom
里能得到545kb/s)。
顺便提一下,如果你在生成密码,可能会想看看pwgen
——它可以帮你生成好读的密码 :).