# py-scrypt **Repository Path**: mirrors_holgern/py-scrypt ## Basic Information - **Project Name**: py-scrypt - **Description**: This is a set of Python bindings for the scrypt key derivation function. - **Primary Language**: Unknown - **License**: BSD-2-Clause - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-11-23 - **Last Updated**: 2026-05-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ========================= Python scrypt_ bindings ========================= This is a set of Python_ bindings for the scrypt_ key derivation function. .. image:: https://img.shields.io/pypi/v/scrypt.svg :target: https://pypi.python.org/pypi/scrypt/ :alt: Latest Version .. image:: https://anaconda.org/conda-forge/scrypt/badges/version.svg :target: https://anaconda.org/conda-forge/scrypt .. image:: https://anaconda.org/conda-forge/scrypt/badges/downloads.svg :target: https://anaconda.org/conda-forge/scrypt Scrypt is useful when encrypting password as it is possible to specify a *minimum* amount of time to use when encrypting and decrypting. If, for example, a password takes 0.05 seconds to verify, a user won't notice the slight delay when signing in, but doing a brute force search of several billion passwords will take a considerable amount of time. This is in contrast to more traditional hash functions such as MD5 or the SHA family which can be implemented extremely fast on cheap hardware. Installation ============ Or you can install the latest release from PyPi:: $ pip install scrypt Users of the Anaconda_ Python distribution can directly obtain pre-built Windows, Intel Linux or macOS / OSX binaries from the conda-forge channel. This can be done via:: $ conda install -c conda-forge scrypt If you want py-scrypt for your Python 3 environment, just run the above commands with your Python 3 interpreter. Py-scrypt supports both Python 2 and 3. From version 0.6.0 (not available on PyPi yet), py-scrypt supports PyPy as well. Build From Source ================= For Debian and Ubuntu, please ensure that the following packages are installed: .. code:: bash $ sudo apt-get install build-essential libssl-dev python-dev For Fedora and RHEL-derivatives, please ensure that the following packages are installed: .. code:: bash $ sudo yum install gcc openssl-devel python-devel For OSX, please do the following:: $ brew install openssl $ export CFLAGS="-I$(brew --prefix openssl)/include $CFLAGS" $ export LDFLAGS="-L$(brew --prefix openssl)/lib $LDFLAGS" For OSX, you can also use the precompiled wheels. They are installed by:: $ pip install scrypt For Windows, please use the precompiled wheels. They are installed by:: $ pip install scrypt For Windows, when the package should be compiled, the development package from https://slproweb.com/products/Win32OpenSSL.html is needed. It needs to be installed to `C:\OpenSSL-Win64` or `C:\Program Files\OpenSSL`. It is also possible to use the Chocolatey package manager to install OpenSSL: ``` choco install openssl ``` You can install py-scrypt from this repository if you want the latest but possibly non-compiling version:: $ git clone https://github.com/holgern/py-scrypt.git $ cd py-scrypt $ python setup.py build Become superuser (or use virtualenv): # python setup.py install Run tests after install: $ python setup.py test Changelog ========= 0.9.1 ----- * No notable change 0.9.0 ----- * Update to scrypt 1.3.3 0.8.29 ------ * Fix build for OSX using openssl 3.0 * Build Wheel for Python 3.13 * switch to ruff 0.8.24 ------ * Building of all wheels works with github actions 0.8.20 ------ * Fix #8 by adding missing gettimeofday.c to MANIFEST.in 0.8.19 ------ * Use RtlGenRandom instead of CryptGenRandom on windows (Thanks to https://github.com/veorq/cryptocoding/) * Add check for c:\Program Files\OpenSSL-Win64 and c:\Program Files\OpenSSL-Win32 0.8.18 ------ * add wheel for python 3.9 0.8.17 ------ * add_dll_directory for python 3.8 on windows, as importlib.util.find_spec does not search all paths anymore 0.8.16 ------ * Add additional test vector from RFC (thanks to @ChrisMacNaughton) 0.8.15 ------ * Fix missing import 0.8.14 ------ * fix imp deprecation warning 0.8.13 ------ * improve build for conda forge 0.8.12 ------ * Add SCRYPT_WINDOWS_LINK_LEGACY_OPENSSL environment variable, when set, openssl 1.0.2 is linked 0.8.11 ------ * fix build for conda feedstock 0.8.10 ------ * fix typo 0.8.9 ----- * use the static libcrypto_static for windows and openssl 1.1.1 0.8.8 ----- * setup.py for windows improved, works with openssl 1.0.2 and 1.1.1 0.8.7 ----- * setup.py for windows fixed 0.8.6 ----- * setup.py fixed, scrypt could not be imported in version 0.8.5 0.8.5 ----- * MANIFEST.in fixed * scrypt.py moved into own scrypt directory with __init__.py * openssl library path for osx wheel repaired 0.8.4 ----- * __version__ added to scrypt * missing void in sha256.c fixed 0.8.3 ----- * scrypt updated to 1.2.1 * Wheels are created for python 3.6 Usage ===== For encryption/decryption, the library exports two functions ``encrypt`` and ``decrypt``:: >>> import scrypt >>> data = scrypt.encrypt('a secret message', 'password', maxtime=0.1) # This will take at least 0.1 seconds >>> data[:20] 'scrypt\x00\r\x00\x00\x00\x08\x00\x00\x00\x01RX9H' >>> scrypt.decrypt(data, 'password', force=True) # This will also take at least 0.1 seconds 'a secret message' >>> scrypt.decrypt(data, 'password', maxtime=0.05) # scrypt won't be able to decrypt this data fast enough Traceback (most recent call last): File "", line 1, in scrypt.error: decrypting file would take too long >>> scrypt.decrypt(data, 'wrong password', force=True) # scrypt will throw an exception if the password is incorrect Traceback (most recent call last): File "", line 1, in scrypt.error: password is incorrect From these, one can make a simple password verifier using the following functions:: import os import scrypt def hash_password(password, maxtime=0.5, datalength=64): """Create a secure password hash using scrypt encryption. Args: password: The password to hash maxtime: Maximum time to spend hashing in seconds datalength: Length of the random data to encrypt Returns: bytes: An encrypted hash suitable for storage and later verification """ return scrypt.encrypt(os.urandom(datalength), password, maxtime=maxtime) def verify_password(hashed_password, guessed_password, maxtime=0.5): """Verify a password against its hash with better error handling. Args: hashed_password: The stored password hash from hash_password() guessed_password: The password to verify maxtime: Maximum time to spend in verification Returns: tuple: (is_valid, status_code) where: - is_valid: True if password is correct, False otherwise - status_code: One of "correct", "wrong_password", "time_limit_exceeded", "memory_limit_exceeded", or "error" Raises: scrypt.error: Only raised for resource limit errors, which you may want to handle by retrying with higher limits or force=True """ try: scrypt.decrypt(hashed_password, guessed_password, maxtime, encoding=None) return True, "correct" except scrypt.error as e: # Check the specific error message to differentiate between causes error_message = str(e) if error_message == "password is incorrect": # Wrong password was provided return False, "wrong_password" elif error_message == "decrypting file would take too long": # Time limit exceeded raise # Re-raise so caller can handle appropriately elif error_message == "decrypting file would take too much memory": # Memory limit exceeded raise # Re-raise so caller can handle appropriately else: # Some other error occurred (corrupted data, etc.) return False, "error" # Example usage: # Create a hash of a password stored_hash = hash_password("correct_password", maxtime=0.1) # Verify with correct password is_valid, status = verify_password(stored_hash, "correct_password", maxtime=0.1) if is_valid: print("Password is correct!") # This will be printed # Verify with wrong password is_valid, status = verify_password(stored_hash, "wrong_password", maxtime=0.1) if not is_valid: if status == "wrong_password": print("Password is incorrect!") # This will be printed # Verify with insufficient time try: # Set maxtime very low to trigger a time limit error is_valid, status = verify_password(stored_hash, "correct_password", maxtime=0.00001) except scrypt.error as e: if "would take too long" in str(e): print("Time limit exceeded, try with higher maxtime or force=True") # Retry with force=True result = scrypt.decrypt(stored_hash, "correct_password", maxtime=0.00001, force=True, encoding=None) print("Forced decryption successful!") The `encrypt` function accepts several parameters to control its behavior:: encrypt(input, password, maxtime=5.0, maxmem=0, maxmemfrac=0.5, logN=0, r=0, p=0, force=False, verbose=False) Where: - `input`: Data to encrypt (bytes or str) - `password`: Password for encryption (bytes or str) - `maxtime`: Maximum time to spend in seconds - `maxmem`: Maximum memory to use in bytes (0 for unlimited) - `maxmemfrac`: Maximum fraction of available memory to use (0.0 to 1.0) - `logN`, `r`, `p`: Parameters controlling the scrypt key derivation function - If all three are zero (default), optimal parameters are chosen automatically - If provided, all three must be non-zero and will be used explicitly - `force`: If True, do not check whether encryption will exceed the estimated memory or time - `verbose`: If True, display parameter information The `decrypt` function has a simpler interface:: decrypt(input, password, maxtime=300.0, maxmem=0, maxmemfrac=0.5, encoding='utf-8', verbose=False, force=False) Where: - `input`: Encrypted data (bytes or str) - `password`: Password for decryption (bytes or str) - `maxtime`: Maximum time to spend in seconds - `maxmem`: Maximum memory to use in bytes (0 for unlimited) - `maxmemfrac`: Maximum fraction of available memory to use - `encoding`: Encoding to use for output string (None for raw bytes) - `verbose`: If True, display parameter information - `force`: If True, do not check whether decryption will exceed the estimated memory or time But, if you want output that is deterministic and constant in size, you can use the ``hash`` function:: >>> import scrypt >>> h1 = scrypt.hash('password', 'random salt') >>> len(h1) # The hash will be 64 bytes by default, but is overridable. 64 >>> h1[:10] '\xfe\x87\xf3hS\tUo\xcd\xc8' >>> h2 = scrypt.hash('password', 'random salt') >>> h1 == h2 # The hash function is deterministic True The `hash` function accepts the following parameters:: hash(password, salt, N=1<<14, r=8, p=1, buflen=64) Where: - `password`: The password to hash (bytes or str) - `salt`: Salt for the hash (bytes or str) - `N`: CPU/memory cost parameter (must be a power of 2) - `r`: Block size parameter - `p`: Parallelization parameter - `buflen`: Output buffer length The parameters r, p, and buflen must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N must be a power of 2 greater than 1. N, r, and p must all be positive. For advanced usage, the library also provides two utility functions: - `pickparams(maxmem=0, maxmemfrac=0.5, maxtime=5.0, verbose=0)`: Automatically chooses optimal scrypt parameters based on system resources. Returns (logN, r, p) tuple. - `checkparams(logN, r, p, maxmem=0, maxmemfrac=0.5, maxtime=5.0, verbose=0, force=0)`: Verifies that the provided parameters are valid and within resource limits. Acknowledgements ================ Scrypt_ was created by Colin Percival and is licensed as 2-clause BSD. Since scrypt does not normally build as a shared library, I have included the source for the currently latest version of the library in this repository. When a new version arrives, I will update these sources. `Kelvin Wong`_ on Bitbucket provided changes to make the library available on Mac OS X 10.6 and earlier, as well as changes to make the library work more like the command-line version of scrypt by default. Kelvin also contributed with the unit tests, lots of cross platform testing and work on the ``hash`` function. Burstaholic_ on Bitbucket provided the necessary changes to make the library build on Windows. The `python-appveyor-demo`_ repository for setting up automated Windows builds for a multitude of Python versions. License ======= This library is licensed under the same license as scrypt; 2-clause BSD. .. _scrypt: http://www.tarsnap.com/scrypt.html .. _Python: http://python.org .. _Burstaholic: https://bitbucket.org/Burstaholic .. _Kelvin Wong: https://bitbucket.org/kelvinwong_ca .. _python-appveyor-demo: https://github.com/ogrisel/python-appveyor-demo .. _Anaconda: https://www.continuum.io