你是对的 - 这两个链接正在使用的 scrypt 函数是 scrypt 文件加密实用程序,而不是底层的 kdf。我一直在慢慢地为 python 创建一个独立的基于 scrypt 的密码哈希,我自己也遇到了这个问题。
scrypt 文件实用程序执行以下操作:选择特定于您的系统的 scrypt 的 n/r/p 参数和“最小时间”参数。然后它生成一个 32 字节的盐,然后调用scrypt(n,r,p,salt,pwd)
创建一个 64 字节的密钥。该工具返回的二进制字符串由以下部分组成: 1) 包含 n、r、p 值和二进制编码的 salt 的标头;2) 标头的 sha256 校验和;3) 校验和的 hmac-sha256 签名副本,使用密钥的前 32 个字节。之后,它使用密钥的剩余 32 个字节对输入数据进行 AES 加密。
我可以看到这有几个含义:
输入数据是没有意义的,因为它实际上并不影响正在使用的盐,并且 encrypt() 每次都会生成一个新的盐。
您不能手动配置 n,r,p 工作负载,或者除了笨拙的 min-time 参数之外的任何其他方式。这不是不安全的,而是控制工作因素的一种相当尴尬的方式。
在解密调用重新生成密钥并将其与 hmac 进行比较之后,如果您的密码错误,它将拒绝那里的所有内容 - 但如果它是正确的,它还将继续解密数据包。这是攻击者不必执行的大量额外工作——他们甚至不必导出 64 个字节,只需检查签名所需的 32 个字节。这个问题并没有使它完全不安全,但是做你的攻击者没有做的工作永远是不可取的。
无法配置盐键、派生键大小等。当前值还不错,但仍然不理想。
解密实用程序的“最大时间”限制对于密码散列是错误的 - 每次调用解密时,它都会估计系统的速度,并就是否可以在最大时间内计算密钥进行一些“猜测” - 这会增加攻击者的开销不必这样做(请参阅#3),但这也意味着解密可能会在系统负载较重的情况下开始拒绝密码。
我不确定为什么Colin Percival 没有将 kdf 和参数选择代码作为公共 api 的一部分,但实际上它在源代码中明确标记为“私有”——甚至没有导出用于链接。这让我犹豫是否直接访问它而不进行更多研究。
总而言之,需要一种可以存储 scrypt 的良好哈希格式,以及公开底层 kdf 和参数选择算法的实现。我目前正在为passlib自己做这件事,但它并没有引起太多关注:(
不过,最重要的是——那些网站的说明是“好的”,我只是使用一个空字符串作为文件内容,并注意额外的开销和问题。