# `crypt` —— 验证 Unix 口令的函数

源代码： Lib/struct.py

3.11 版后已移除: `crypt` 模块已被弃用（请参阅 PEP 594 了解详情及其替代品）。 `hashlib` 模块是针对特定用例的潜在替换选项。

本模块实现了连接 crypt(3) 的接口，是一个基于改进 DES 算法的单向散列函数；更多细节请参阅 Unix man 手册。可能的用途包括保存经过哈希的口令，这样就可以在不存储实际口令的情况下对其进行验证，或者尝试用字典来破解 Unix 口令。

请注意，本模块的执行取决于当前系统中 crypt(3) 的实际实现。 因此，当前实现版本可用的扩展均可在本模块使用。

可用性 ：Unix。在 VxWorks 中不可用。

## 哈希方法

3.3 新版功能.

`crypt` 模块定义了哈希方法的列表（不是所有的方法在所有平台上都可用）。

`crypt.``METHOD_SHA512`

  基于 SHA-512 哈希函数的模块化加密格式方法，具备 16 个字符的 salt 和 86个字符的哈希算法。这是最强的哈希算法。

`crypt.``METHOD_SHA256`

  另一种基于 SHA-256 哈希函数的模块化加密格式方法，具备 16 个字符的 salt 和 43 个字符的哈希算法。

`crypt.``METHOD_BLOWFISH`

  另一种基于 Blowfish 的模块化加密格式方法，有 22 个字符的 salt 和 31 个字符的哈希算法。

  3.7 新版功能.

`crypt.``METHOD_MD5`

  另一种基于 MD5 哈希函数的模块化加密格式方法，具备 8 个字符的 salt 和 22 个字符的哈希算法。

`crypt.``METHOD_CRYPT`

  传统的方法，具备 2 个字符的 salt 和 13 个字符的哈希算法。这是最弱的方法。

## 模块属性

3.3 新版功能.

`crypt.``methods`

  可用口令哈希算法的列表，形式为 `crypt.METHOD_*` 对象。该列表从最强到最弱进行排序。

## 模块函数

`crypt` 模块定义了以下函数：

`crypt.``crypt`(word, salt=None)

  word 通常将是用户在提示符或图形界面上输入的口令。 可选参数 salt 要么是 `mksalt()` 所返回的字符串，即 `crypt.METHOD_*` 值之一（尽管不是在所有平台上都可用），要么就是一个包括 salt 的完全加密的口令，与本函数的返回值一样。 如果未给出 salt，则将使用 `methods` 中提供的最强方法。

  查验口令通常是传入纯文本密码 word ，和之前 `crypt()` 调用的结果进行比较，应该与本次调用的结果相同。

  salt (随机的 2 或 16 个字符的字符串，可能带有 `$digit{TX-PL-LABEL}#x60;` 前缀以提示相关方法) 将被用来扰乱加密算法。 salt 中的字符必须在 `[./a-zA-Z0-9]` 集合中，但 Modular Crypt Format 除外，它会带有 `$digit{TX-PL-LABEL}#x60;` 前缀。

  返回哈希后的口令字符串，将由 salt 所在字母表中的字符组成。

  由于有些 crypt(3) 扩展可以接受各种大小的 salt 值，建议在查验口令时采用完整的加密后口令作为 salt。

  在 3.3 版更改: 除了字符串之外， salt 还可接受 `crypt.METHOD_*` 值。

`crypt.``mksalt`(method=None, *, rounds=None)

  返回用指定方法随机生成的 salt 值。 如果没有给出 method，则会使用 `methods` 中提供的最强方法。is used.

  返回一个字符串，可用作传入 `crypt()` 的 salt 参数。

  rounds 指定了 `METHOD_SHA256`, `METHOD_SHA512` 和 `METHOD_BLOWFISH` 的循环次数。 对于 `METHOD_SHA256` 和 `METHOD_SHA512` 而言，必须为介于 `1000` 和 `999_999_999` 之间的整数，默认值为 `5000`。 而对于 `METHOD_BLOWFISH`，则必须为 `16` (24) 和 `2_147_483_648` (231) 之间的二的幂，默认值为 `4096` (212)。

  3.3 新版功能.

  在 3.7 版更改: 加入 rounds 参数。

## 例子

以下简单示例演示了典型用法（需要一个时间固定的比较操作来限制留给计时攻击的暴露面。 `hmac.compare_digest()` 即很适用）：

    import pwd
    import crypt
    import getpass
    from hmac import compare_digest as compare_hash

    def login():
        username = input('Python login: ')
        cryptedpasswd = pwd.getpwnam(username)[1]
        if cryptedpasswd:
            if cryptedpasswd == 'x' or cryptedpasswd == '*':
                raise ValueError('no support for shadow passwords')
            cleartext = getpass.getpass()
            return compare_hash(crypt.crypt(cleartext, cryptedpasswd), cryptedpasswd)
        else:
            return True

采用当前强度最高的方法生成哈希值，并与原口令进行核对：

    import crypt
    from hmac import compare_digest as compare_hash

    hashed = crypt.crypt(plaintext)
    if not compare_hash(hashed, crypt.crypt(plaintext, hashed)):
        raise ValueError("hashed version doesn't validate against original")