I use an up to date Debian 10. I have unexpected behavior with python crypt.crypt
, some calls raise an OSError: Invalid Argument
exception, without further explanation, so I am not really sure what is happening.
>>> # debian - python3.9
>>> import crypt
>>> crypt.crypt("foo", '$2a$10$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", '$2a$10$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7')
'$2a$10$Ud3Zhyb1M3I1PMDXWOFBp.8U7GsUrAN2JZZbKcxHSc.cTrK6oEA/.'
This code fails silently on my debian with the system python3.7 (it returns None
until python3.9) and raises this OSError
on my manually built python3.9. It works perfectly well on my archlinux on any python version.
edit1: I suspect this is due to the crypt methods maybe being not supported anymore with debian?
# 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: The crypt man page shows this interesting piece of information:
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)
So the $2a
at the start of the salt would mean that blowfish is needed for crypt to work properly, but it is not available on debian according to crypt.method
.
edit3: I can confirm this with this piece of C code
// 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 = "$2a$10$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7";
crypt_result = crypt(word, salt);
if (crypt_result == NULL) {
printf("%s\n", strerror(errno));
}
else {
printf("%s\n", crypt_result);
}
return 0;
}
It displays the expected output on archlinux, but displays Invalid argument
on debian.
How can I solve this situation?
The $2a
at the beginning of the salt is a marker of the blowfish/bcrypt method.
Blowfish is not supported by all Linux distros, like Ubuntu:
How to make Ubuntu's crypt(3) support Blowfish?
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1349252
Debian does not support blowfish in crypt as of Debian 10 but should be able to do it in the next release, moving the dependency of crypt from glibc to libxcrypt:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=149452#42
https://blog.bofh.it/debian/id_458
I made an attempt to use passlib with the verify
context whose prototype is like crypt.crypt
:
>>> from passlib.apps import custom_app_context as pwd_context
>>> pwd_context.verify("foo", '$2a$10$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7')
but unfortunately this snippet code seems to be bugged.
For now I will stick to crypt.crypt
and use bcrypt as a fallback:
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", "$2a$10$Ud3Zhyb1M3I1PMDXWOFBp.81LKFwD5mZo33jnVHbPPrQY4cKSqoI7")