crypt.crypt raises an OSError: Invalid Argument

crypt.crypt raises an OSError: Invalid Argument

我使用的是最新的 Debian 10。我在使用 python crypt.crypt 时出现意外行为,一些调用引发了 OSError: Invalid Argument 异常,没有进一步的解释,所以我不是真的确定发生了什么。

>>> # debian - python3.9
>>> import crypt
>>> crypt.crypt("foo", 'a$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/crypt.py", line 82, in crypt
    return _crypt.crypt(word, salt)
OSError: [Errno 22] Invalid argument

>>> # archlinux - python3.9
>>> import crypt
>>> crypt.crypt("foo", 'a$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7')
'a$Ud3Zhyb1M3I1PMDXWOFBp.8U7GsUrAN2JZZbKcxHSc.cTrK6oEA/.'

此代码在我的 debian 系统 python3.7 (it returns None until python3.9) 上静默失败,并在我手动构建的 python3.9 上引发此 OSError。它在我的任何 python 版本的 archlinux 上都运行良好。

edit1:我怀疑这是因为 debian 可能不再支持 crypt 方法?

# On arch
>>> crypt.methods
[<crypt.METHOD_SHA512>, <crypt.METHOD_SHA256>, <crypt.METHOD_BLOWFISH>, <crypt.METHOD_MD5>, <crypt.METHOD_CRYPT>]

# On Debian
>>> crypt.methods
[<crypt.METHOD_SHA512>, <crypt.METHOD_SHA256>, <crypt.METHOD_MD5>, <crypt.METHOD_CRYPT>]

edit2:crypt 手册页显示了这条有趣的信息:

If  salt  is  a character string starting with the characters "$id$" followed by a string
optionally terminated by "$", then the result has the form:

       $id$salt$encrypted

id identifies the encryption method used instead of DES and this then determines how
the rest of the password  string  is  interpreted.

The following values of id are supported:
ID  | Method
─────────────────────────────────────────────────────────
1   | MD5
2a  | Blowfish (not in mainline glibc; added in some
    | Linux distributions)
5   | SHA-256 (since glibc 2.7)
6   | SHA-512 (since glibc 2.7)

所以 salt 开头的 a 意味着 crypt 需要河豚才能正常工作,但根据 crypt.method.

它在 debian 上不可用

edit3:我可以用这段 C 代码确认这一点

// gcc crypt.c -o foobar -lcrypt; ./foobar
#include <crypt.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>


int main() {
    char *crypt_result;
    char* word = "foo";
    char* salt = "a$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7";

    crypt_result = crypt(word, salt);
    if (crypt_result == NULL) {
        printf("%s\n", strerror(errno));
    }
    else {
        printf("%s\n", crypt_result);
    }

    return 0;
}

它在 archlinux 上显示预期的输出,但在 debian 上显示 Invalid argument

我该如何解决这种情况?

更多上下文

salt开头的a是blowfish/bcrypt方法的标记

并非所有 Linux 发行版都支持 Blowfish,例如 Ubuntu:

How to make Ubuntu's crypt(3) support Blowfish?

https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1349252

从 Debian 10 开始,Debian 不支持 crypt 中的河豚,但应该能够在下一个版本中支持,将 crypt 的依赖项从 glibc 移至 libxcrypt:

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=149452#42

https://blog.bofh.it/debian/id_458

如何解决这个问题

尝试使用 passlib

我尝试将 passlibverify 上下文一起使用,其原型类似于 crypt.crypt:

>>> from passlib.apps import custom_app_context as pwd_context
>>> pwd_context.verify("foo", 'a$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7')

但不幸的是这段代码 seems to be bugged

尝试使用 bcrypt

现在我会坚持使用 crypt.crypt 并使用 bcrypt 作为后备:

import crypt
import bcrypt

def verify(password, hash):
    try:
        return crypt.crypt(password, hash) == hash
    except OSError:
        return bcrypt.checkpw(password.encode("utf-8"), hash.encode("utf-8"))

verify("foo", "a$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7")