Add utility to replace crypt.crypt

The crypt module was removed in Python 3.13. Add the utility function
to replace crypt.crypt which is used by a few projects. Note that
the function internally calls libcrypt library, so that we don't have
to re-implement its algorithm in python layer.

Change-Id: Ibe6748068b8145fdc7ece384c94a923d1032de5f
This commit is contained in:
Takashi Kajinami 2024-10-09 19:39:03 +09:00
parent db58371f0e
commit 6ef3f1e598
3 changed files with 37 additions and 0 deletions

View File

@ -9,3 +9,7 @@ python3-devel [platform:rpm]
qemu-img [platform:redhat test]
qemu-tools [platform:suse test]
qemu-utils [platform:dpkg test]
libxcrypt-compat [platform:redhat crypt]
libcrypt1 [platform:suse crypt]
libcrypt1 [platform:dpkg crypt]

View File

@ -18,6 +18,8 @@ Secret utilities.
.. versionadded:: 3.5
"""
import ctypes
import ctypes.util
import hashlib
import hmac
@ -40,3 +42,25 @@ def md5(string=b'', usedforsecurity=True):
See https://bugs.python.org/issue9216
"""
return hashlib.md5(string, usedforsecurity=usedforsecurity) # nosec
if ctypes.util.find_library("crypt"):
_libcrypt = ctypes.CDLL(ctypes.util.find_library("crypt"), use_errno=True)
_crypt = _libcrypt.crypt
_crypt.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
_crypt.restype = ctypes.c_char_p
else:
_crypt = None
def crypt_password(key, salt):
"""Encrtpt password string and generate the value in /etc/shadow format
This is provided as a replacement of crypt.crypt method because crypt
module was removed in Python 3.13.
.. versionadded:: 7.5
"""
if _crypt is None:
raise RuntimeError('libcrypt is not available')
return _crypt(key.encode('utf-8'), salt.encode('utf-8')).decode('utf-8')

View File

@ -77,3 +77,12 @@ class SecretUtilsTest(testscenarios.TestWithScenarios,
TypeError, secretutils.md5, None, usedforsecurity=True)
self.assertRaises(
TypeError, secretutils.md5, None, usedforsecurity=False)
def test_password_crypt(self):
self.assertEqual(
'$5$mysalt$fcnMdhaFpUmeWtGOgVuImueZGL1v0Q1kUVbV2NbFOX4',
secretutils.crypt_password('mytopsecret', '$5$mysalt$'))
self.assertEqual(
'$6$mysalt$jTEJ24XtvcWmav/sTQb1tYqmk1kBQD/sxcMIxEPUcie'
'J8L9AuCTWxYlxGz.XtIQYWspWkUXQz9zPIFTSKubP6.',
secretutils.crypt_password('mytopsecret', '$6$mysalt$'))