Created
February 16, 2026 14:43
-
-
Save cfra/a839043328c0f690b43eed44ac7130d5 to your computer and use it in GitHub Desktop.
Create crypt(3) passwords with Python >= 3.13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # | |
| # Copyright 2026 Christian Franke <nobody@nowhere.ws> | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| # This small script was created as Python 3.13 dropped the crypt module and | |
| # Python is my ad-hoc way to call the crypt(3) function of the OS. | |
| # | |
| # The script can either be imported as module, then it exposes the crypt | |
| # and crypt_gensalt methods of the underlying OS. | |
| # | |
| # It can also be executed directly, in this case, it will either take a password | |
| # passed as argument or one provided interactively and hash it. | |
| # | |
| import cffi | |
| import getpass | |
| import os | |
| import sys | |
| _ffi = cffi.FFI() | |
| _ffi.cdef("char *crypt(const char *phrase, const char *setting);") | |
| _ffi.cdef("char *crypt_gensalt(const char *prefix, unsigned long count, const char *rbytes, int nrbytes);") | |
| _crypt = _ffi.dlopen("crypt") | |
| def crypt(phrase, setting=None): | |
| if hasattr(phrase, "encode"): | |
| phrase = phrase.encode('utf8') | |
| if hasattr(setting, "encode"): | |
| setting = setting.encode('ascii') | |
| rv = _crypt.crypt(phrase, setting) | |
| if rv == _ffi.NULL: | |
| raise OSError(_ffi.errno, os.strerror(_ffi.errno)) | |
| return _ffi.string(rv).decode('ascii') | |
| def crypt_gensalt(prefix, count): | |
| if hasattr(prefix, "encode"): | |
| prefix = prefix.encode('ascii') | |
| if prefix is None: | |
| prefix = _ffi.NULL | |
| rv = _crypt.crypt_gensalt(prefix, count, _ffi.NULL, 0) | |
| if rv == _ffi.NULL: | |
| raise OSError(_ffi.errno, os.strerror(_ffi.errno)) | |
| return _ffi.string(rv).decode('ascii') | |
| if __name__ == '__main__': | |
| if len(sys.argv) == 2: | |
| password = sys.argv[1] | |
| else: | |
| password = getpass.getpass() | |
| print(crypt(password, crypt_gensalt("$6$", 65536))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment