10

我想使用 scrypt 为我的用户密码和盐创建一个哈希。我找到了两个 参考资料,但有些事情我不明白。

他们使用 scrypt 加密和解密功能。一个加密一个随机字符串,另一个加密盐(这看起来是错误的,因为只有密码而不是盐用于解密)。看起来解密功能正在用于验证密码/盐作为解密的副作用。

根据我的了解,我想要的是密钥派生函数(KDF)而不是加密/解密,并且 KDF 可能由 scrypt 生成并用于加密/解密。实际的 KDF 在幕后使用,我担心盲目地遵循这些示例会导致错误。如果使用 scrypt 加密/解密函数来生成和验证密码,我不明白被加密字符串的作用。它的内容或长度重要吗?

4

2 回答 2

11

你是对的 - 这两个链接正在使用的 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 加密。

我可以看到这有几个含义:

  1. 输入数据是没有意义的,因为它实际上并不影响正在使用的盐,并且 encrypt() 每次都会生成一个新的盐。

  2. 您不能手动配置 n,r,p 工作负载,或者除了笨拙的 min-time 参数之外的任何其他方式。这不是不安全的,而是控制工作因素的一种相当尴尬的方式。

  3. 在解密调用重新生成密钥并将其与 hmac 进行比较之后,如果您的密码错误,它将拒绝那里的所有内容 - 但如果它是正确的,它还将继续解密数据包。这是攻击者不必执行的大量额外工作——他们甚至不必导出 64 个字节,只需检查签名所需的 32 个字节。这个问题并没有使它完全不安全,但是做你的攻击者没有做的工作永远是不可取的。

  4. 无法配置盐键、派生键大小等。当前值还不错,但仍然不理想。

  5. 解密实用程序的“最大时间”限制对于密码散列是错误的 - 每次调用解密时,它都会估计系统的速度,并就是否可以在最大时间内计算密钥进行一些“猜测” - 这会增加攻击者的开销不必这样做(请参阅#3),但这也意味着解密可能会在系统负载较重的情况下开始拒绝密码。

  6. 我不确定为什么Colin Percival 没有将 kdf 和参数选择代码作为公共 api 的一部分,但实际上它在源代码中明确标记为“私有”——甚至没有导出用于链接。这让我犹豫是否直接访问它而不进行更多研究。

总而言之,需要一种可以存储 scrypt 的良好哈希格式,以及公开底层 kdf 和参数选择算法的实现。我目前正在为passlib自己做这件事,但它并没有引起太多关注:(

不过,最重要的是——那些网站的说明是“好的”,我只是使用一个空字符串作为文件内容,并注意额外的开销和问题。

于 2012-12-01T20:17:21.693 回答
6

这两个参考都完全错误。不要搞砸encryptand decrypt:只需使用hash

KDF 没有直接暴露,但hash足够接近。(事实上​​,在我看来更好,因为它混合了 PBKDF2 三明治的馅料。)

此示例代码适用于 python2.7 和 python3.2。它使用 PyCrypto、passlib 和 py-scrypt,但只需要py-scrypt。

您将需要使用恒定时间比较功能,例如passlib.utils.consteq减轻定时攻击。

您还需要仔细选择参数。默认值 logN=14,r=8,p=1 表示使用 16 MiB 内存进行 1 个“回合”。在服务器上,您可能需要更像 10、8、8 的东西——更少的 RAM,更多的 CPU。应该在您的硬件上按预期负载计时。

于 2013-09-26T14:26:18.510 回答