diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py b/.venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py new file mode 100644 index 00000000..0c4cc1a2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py @@ -0,0 +1,600 @@ +# Copyright 2013 Donald Stufft and individual contributors +# +# 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. + +import sys +from typing import Tuple + +import nacl.exceptions as exc +from nacl._sodium import ffi, lib +from nacl.exceptions import ensure + + +has_crypto_pwhash_scryptsalsa208sha256 = bool( + lib.PYNACL_HAS_CRYPTO_PWHASH_SCRYPTSALSA208SHA256 +) + +crypto_pwhash_scryptsalsa208sha256_STRPREFIX = b"" +crypto_pwhash_scryptsalsa208sha256_SALTBYTES = 0 +crypto_pwhash_scryptsalsa208sha256_STRBYTES = 0 +crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN = 0 +crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX = 0 +crypto_pwhash_scryptsalsa208sha256_BYTES_MIN = 0 +crypto_pwhash_scryptsalsa208sha256_BYTES_MAX = 0 +crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN = 0 +crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX = 0 +crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN = 0 +crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX = 0 +crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE = 0 +crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE = 0 +crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE = 0 +crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE = 0 + +if has_crypto_pwhash_scryptsalsa208sha256: + crypto_pwhash_scryptsalsa208sha256_STRPREFIX = ffi.string( + ffi.cast("char *", lib.crypto_pwhash_scryptsalsa208sha256_strprefix()) + )[:] + crypto_pwhash_scryptsalsa208sha256_SALTBYTES = ( + lib.crypto_pwhash_scryptsalsa208sha256_saltbytes() + ) + crypto_pwhash_scryptsalsa208sha256_STRBYTES = ( + lib.crypto_pwhash_scryptsalsa208sha256_strbytes() + ) + crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN = ( + lib.crypto_pwhash_scryptsalsa208sha256_passwd_min() + ) + crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX = ( + lib.crypto_pwhash_scryptsalsa208sha256_passwd_max() + ) + crypto_pwhash_scryptsalsa208sha256_BYTES_MIN = ( + lib.crypto_pwhash_scryptsalsa208sha256_bytes_min() + ) + crypto_pwhash_scryptsalsa208sha256_BYTES_MAX = ( + lib.crypto_pwhash_scryptsalsa208sha256_bytes_max() + ) + crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN = ( + lib.crypto_pwhash_scryptsalsa208sha256_memlimit_min() + ) + crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX = ( + lib.crypto_pwhash_scryptsalsa208sha256_memlimit_max() + ) + crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN = ( + lib.crypto_pwhash_scryptsalsa208sha256_opslimit_min() + ) + crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX = ( + lib.crypto_pwhash_scryptsalsa208sha256_opslimit_max() + ) + crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE = ( + lib.crypto_pwhash_scryptsalsa208sha256_opslimit_interactive() + ) + crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE = ( + lib.crypto_pwhash_scryptsalsa208sha256_memlimit_interactive() + ) + crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE = ( + lib.crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive() + ) + crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE = ( + lib.crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive() + ) + +crypto_pwhash_ALG_ARGON2I13: int = lib.crypto_pwhash_alg_argon2i13() +crypto_pwhash_ALG_ARGON2ID13: int = lib.crypto_pwhash_alg_argon2id13() +crypto_pwhash_ALG_DEFAULT: int = lib.crypto_pwhash_alg_default() + +crypto_pwhash_SALTBYTES: int = lib.crypto_pwhash_saltbytes() +crypto_pwhash_STRBYTES: int = lib.crypto_pwhash_strbytes() + +crypto_pwhash_PASSWD_MIN: int = lib.crypto_pwhash_passwd_min() +crypto_pwhash_PASSWD_MAX: int = lib.crypto_pwhash_passwd_max() +crypto_pwhash_BYTES_MIN: int = lib.crypto_pwhash_bytes_min() +crypto_pwhash_BYTES_MAX: int = lib.crypto_pwhash_bytes_max() + +crypto_pwhash_argon2i_STRPREFIX: bytes = ffi.string( + ffi.cast("char *", lib.crypto_pwhash_argon2i_strprefix()) +)[:] +crypto_pwhash_argon2i_MEMLIMIT_MIN: int = ( + lib.crypto_pwhash_argon2i_memlimit_min() +) +crypto_pwhash_argon2i_MEMLIMIT_MAX: int = ( + lib.crypto_pwhash_argon2i_memlimit_max() +) +crypto_pwhash_argon2i_OPSLIMIT_MIN: int = ( + lib.crypto_pwhash_argon2i_opslimit_min() +) +crypto_pwhash_argon2i_OPSLIMIT_MAX: int = ( + lib.crypto_pwhash_argon2i_opslimit_max() +) +crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE: int = ( + lib.crypto_pwhash_argon2i_opslimit_interactive() +) +crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE: int = ( + lib.crypto_pwhash_argon2i_memlimit_interactive() +) +crypto_pwhash_argon2i_OPSLIMIT_MODERATE: int = ( + lib.crypto_pwhash_argon2i_opslimit_moderate() +) +crypto_pwhash_argon2i_MEMLIMIT_MODERATE: int = ( + lib.crypto_pwhash_argon2i_memlimit_moderate() +) +crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE: int = ( + lib.crypto_pwhash_argon2i_opslimit_sensitive() +) +crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE: int = ( + lib.crypto_pwhash_argon2i_memlimit_sensitive() +) + +crypto_pwhash_argon2id_STRPREFIX: bytes = ffi.string( + ffi.cast("char *", lib.crypto_pwhash_argon2id_strprefix()) +)[:] +crypto_pwhash_argon2id_MEMLIMIT_MIN: int = ( + lib.crypto_pwhash_argon2id_memlimit_min() +) +crypto_pwhash_argon2id_MEMLIMIT_MAX: int = ( + lib.crypto_pwhash_argon2id_memlimit_max() +) +crypto_pwhash_argon2id_OPSLIMIT_MIN: int = ( + lib.crypto_pwhash_argon2id_opslimit_min() +) +crypto_pwhash_argon2id_OPSLIMIT_MAX: int = ( + lib.crypto_pwhash_argon2id_opslimit_max() +) +crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE: int = ( + lib.crypto_pwhash_argon2id_opslimit_interactive() +) +crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE: int = ( + lib.crypto_pwhash_argon2id_memlimit_interactive() +) +crypto_pwhash_argon2id_OPSLIMIT_MODERATE: int = ( + lib.crypto_pwhash_argon2id_opslimit_moderate() +) +crypto_pwhash_argon2id_MEMLIMIT_MODERATE: int = ( + lib.crypto_pwhash_argon2id_memlimit_moderate() +) +crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE: int = ( + lib.crypto_pwhash_argon2id_opslimit_sensitive() +) +crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE: int = ( + lib.crypto_pwhash_argon2id_memlimit_sensitive() +) + +SCRYPT_OPSLIMIT_INTERACTIVE = ( + crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE +) +SCRYPT_MEMLIMIT_INTERACTIVE = ( + crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE +) +SCRYPT_OPSLIMIT_SENSITIVE = ( + crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE +) +SCRYPT_MEMLIMIT_SENSITIVE = ( + crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE +) +SCRYPT_SALTBYTES = crypto_pwhash_scryptsalsa208sha256_SALTBYTES +SCRYPT_STRBYTES = crypto_pwhash_scryptsalsa208sha256_STRBYTES + +SCRYPT_PR_MAX = (1 << 30) - 1 +LOG2_UINT64_MAX = 63 +UINT64_MAX = (1 << 64) - 1 +SCRYPT_MAX_MEM = 32 * (1024 * 1024) + + +def _check_memory_occupation( + n: int, r: int, p: int, maxmem: int = SCRYPT_MAX_MEM +) -> None: + ensure(r != 0, "Invalid block size", raising=exc.ValueError) + + ensure(p != 0, "Invalid parallelization factor", raising=exc.ValueError) + + ensure( + (n & (n - 1)) == 0, + "Cost factor must be a power of 2", + raising=exc.ValueError, + ) + + ensure(n > 1, "Cost factor must be at least 2", raising=exc.ValueError) + + ensure( + p <= SCRYPT_PR_MAX / r, + "p*r is greater than {}".format(SCRYPT_PR_MAX), + raising=exc.ValueError, + ) + + ensure(n < (1 << (16 * r)), raising=exc.ValueError) + + Blen = p * 128 * r + + i = UINT64_MAX / 128 + + ensure(n + 2 <= i / r, raising=exc.ValueError) + + Vlen = 32 * r * (n + 2) * 4 + + ensure(Blen <= UINT64_MAX - Vlen, raising=exc.ValueError) + + ensure(Blen <= sys.maxsize - Vlen, raising=exc.ValueError) + + ensure( + Blen + Vlen <= maxmem, + "Memory limit would be exceeded with the choosen n, r, p", + raising=exc.ValueError, + ) + + +def nacl_bindings_pick_scrypt_params( + opslimit: int, memlimit: int +) -> Tuple[int, int, int]: + """Python implementation of libsodium's pickparams""" + + if opslimit < 32768: + opslimit = 32768 + + r = 8 + + if opslimit < (memlimit // 32): + p = 1 + maxn = opslimit // (4 * r) + for n_log2 in range(1, 63): # pragma: no branch + if (2 ** n_log2) > (maxn // 2): + break + else: + maxn = memlimit // (r * 128) + for n_log2 in range(1, 63): # pragma: no branch + if (2 ** n_log2) > maxn // 2: + break + + maxrp = (opslimit // 4) // (2 ** n_log2) + + if maxrp > 0x3FFFFFFF: # pragma: no cover + maxrp = 0x3FFFFFFF + + p = maxrp // r + + return n_log2, r, p + + +def crypto_pwhash_scryptsalsa208sha256_ll( + passwd: bytes, + salt: bytes, + n: int, + r: int, + p: int, + dklen: int = 64, + maxmem: int = SCRYPT_MAX_MEM, +) -> bytes: + """ + Derive a cryptographic key using the ``passwd`` and ``salt`` + given as input. + + The work factor can be tuned by by picking different + values for the parameters + + :param bytes passwd: + :param bytes salt: + :param bytes salt: *must* be *exactly* :py:const:`.SALTBYTES` long + :param int dklen: + :param int opslimit: + :param int n: + :param int r: block size, + :param int p: the parallelism factor + :param int maxmem: the maximum available memory available for scrypt's + operations + :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. + """ + ensure( + has_crypto_pwhash_scryptsalsa208sha256, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + + ensure(isinstance(n, int), raising=TypeError) + ensure(isinstance(r, int), raising=TypeError) + ensure(isinstance(p, int), raising=TypeError) + + ensure(isinstance(passwd, bytes), raising=TypeError) + ensure(isinstance(salt, bytes), raising=TypeError) + + _check_memory_occupation(n, r, p, maxmem) + + buf = ffi.new("uint8_t[]", dklen) + + ret = lib.crypto_pwhash_scryptsalsa208sha256_ll( + passwd, len(passwd), salt, len(salt), n, r, p, buf, dklen + ) + + ensure( + ret == 0, + "Unexpected failure in key derivation", + raising=exc.RuntimeError, + ) + + return ffi.buffer(ffi.cast("char *", buf), dklen)[:] + + +def crypto_pwhash_scryptsalsa208sha256_str( + passwd: bytes, + opslimit: int = SCRYPT_OPSLIMIT_INTERACTIVE, + memlimit: int = SCRYPT_MEMLIMIT_INTERACTIVE, +) -> bytes: + """ + Derive a cryptographic key using the ``passwd`` and ``salt`` + given as input, returning a string representation which includes + the salt and the tuning parameters. + + The returned string can be directly stored as a password hash. + + See :py:func:`.crypto_pwhash_scryptsalsa208sha256` for a short + discussion about ``opslimit`` and ``memlimit`` values. + + :param bytes passwd: + :param int opslimit: + :param int memlimit: + :return: serialized key hash, including salt and tuning parameters + :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. + """ + ensure( + has_crypto_pwhash_scryptsalsa208sha256, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + + buf = ffi.new("char[]", SCRYPT_STRBYTES) + + ret = lib.crypto_pwhash_scryptsalsa208sha256_str( + buf, passwd, len(passwd), opslimit, memlimit + ) + + ensure( + ret == 0, + "Unexpected failure in password hashing", + raising=exc.RuntimeError, + ) + + return ffi.string(buf) + + +def crypto_pwhash_scryptsalsa208sha256_str_verify( + passwd_hash: bytes, passwd: bytes +) -> bool: + """ + Verifies the ``passwd`` against the ``passwd_hash`` that was generated. + Returns True or False depending on the success + + :param passwd_hash: bytes + :param passwd: bytes + :rtype: boolean + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. + """ + ensure( + has_crypto_pwhash_scryptsalsa208sha256, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + + ensure( + len(passwd_hash) == SCRYPT_STRBYTES - 1, + "Invalid password hash", + raising=exc.ValueError, + ) + + ret = lib.crypto_pwhash_scryptsalsa208sha256_str_verify( + passwd_hash, passwd, len(passwd) + ) + ensure(ret == 0, "Wrong password", raising=exc.InvalidkeyError) + # all went well, therefore: + return True + + +def _check_argon2_limits_alg(opslimit: int, memlimit: int, alg: int) -> None: + + if alg == crypto_pwhash_ALG_ARGON2I13: + if memlimit < crypto_pwhash_argon2i_MEMLIMIT_MIN: + raise exc.ValueError( + "memlimit must be at least {} bytes".format( + crypto_pwhash_argon2i_MEMLIMIT_MIN + ) + ) + elif memlimit > crypto_pwhash_argon2i_MEMLIMIT_MAX: + raise exc.ValueError( + "memlimit must be at most {} bytes".format( + crypto_pwhash_argon2i_MEMLIMIT_MAX + ) + ) + if opslimit < crypto_pwhash_argon2i_OPSLIMIT_MIN: + raise exc.ValueError( + "opslimit must be at least {}".format( + crypto_pwhash_argon2i_OPSLIMIT_MIN + ) + ) + elif opslimit > crypto_pwhash_argon2i_OPSLIMIT_MAX: + raise exc.ValueError( + "opslimit must be at most {}".format( + crypto_pwhash_argon2i_OPSLIMIT_MAX + ) + ) + + elif alg == crypto_pwhash_ALG_ARGON2ID13: + if memlimit < crypto_pwhash_argon2id_MEMLIMIT_MIN: + raise exc.ValueError( + "memlimit must be at least {} bytes".format( + crypto_pwhash_argon2id_MEMLIMIT_MIN + ) + ) + elif memlimit > crypto_pwhash_argon2id_MEMLIMIT_MAX: + raise exc.ValueError( + "memlimit must be at most {} bytes".format( + crypto_pwhash_argon2id_MEMLIMIT_MAX + ) + ) + if opslimit < crypto_pwhash_argon2id_OPSLIMIT_MIN: + raise exc.ValueError( + "opslimit must be at least {}".format( + crypto_pwhash_argon2id_OPSLIMIT_MIN + ) + ) + elif opslimit > crypto_pwhash_argon2id_OPSLIMIT_MAX: + raise exc.ValueError( + "opslimit must be at most {}".format( + crypto_pwhash_argon2id_OPSLIMIT_MAX + ) + ) + else: + raise exc.TypeError("Unsupported algorithm") + + +def crypto_pwhash_alg( + outlen: int, + passwd: bytes, + salt: bytes, + opslimit: int, + memlimit: int, + alg: int, +) -> bytes: + """ + Derive a raw cryptographic key using the ``passwd`` and the ``salt`` + given as input to the ``alg`` algorithm. + + :param outlen: the length of the derived key + :type outlen: int + :param passwd: The input password + :type passwd: bytes + :param salt: + :type salt: bytes + :param opslimit: computational cost + :type opslimit: int + :param memlimit: memory cost + :type memlimit: int + :param alg: algorithm identifier + :type alg: int + :return: derived key + :rtype: bytes + """ + ensure(isinstance(outlen, int), raising=exc.TypeError) + ensure(isinstance(opslimit, int), raising=exc.TypeError) + ensure(isinstance(memlimit, int), raising=exc.TypeError) + ensure(isinstance(alg, int), raising=exc.TypeError) + ensure(isinstance(passwd, bytes), raising=exc.TypeError) + + if len(salt) != crypto_pwhash_SALTBYTES: + raise exc.ValueError( + "salt must be exactly {} bytes long".format( + crypto_pwhash_SALTBYTES + ) + ) + + if outlen < crypto_pwhash_BYTES_MIN: + raise exc.ValueError( + "derived key must be at least {} bytes long".format( + crypto_pwhash_BYTES_MIN + ) + ) + + elif outlen > crypto_pwhash_BYTES_MAX: + raise exc.ValueError( + "derived key must be at most {} bytes long".format( + crypto_pwhash_BYTES_MAX + ) + ) + + _check_argon2_limits_alg(opslimit, memlimit, alg) + + outbuf = ffi.new("unsigned char[]", outlen) + + ret = lib.crypto_pwhash( + outbuf, outlen, passwd, len(passwd), salt, opslimit, memlimit, alg + ) + + ensure( + ret == 0, + "Unexpected failure in key derivation", + raising=exc.RuntimeError, + ) + + return ffi.buffer(outbuf, outlen)[:] + + +def crypto_pwhash_str_alg( + passwd: bytes, + opslimit: int, + memlimit: int, + alg: int, +) -> bytes: + """ + Derive a cryptographic key using the ``passwd`` given as input + and a random salt, returning a string representation which + includes the salt, the tuning parameters and the used algorithm. + + :param passwd: The input password + :type passwd: bytes + :param opslimit: computational cost + :type opslimit: int + :param memlimit: memory cost + :type memlimit: int + :param alg: The algorithm to use + :type alg: int + :return: serialized derived key and parameters + :rtype: bytes + """ + ensure(isinstance(opslimit, int), raising=TypeError) + ensure(isinstance(memlimit, int), raising=TypeError) + ensure(isinstance(passwd, bytes), raising=TypeError) + + _check_argon2_limits_alg(opslimit, memlimit, alg) + + outbuf = ffi.new("char[]", 128) + + ret = lib.crypto_pwhash_str_alg( + outbuf, passwd, len(passwd), opslimit, memlimit, alg + ) + + ensure( + ret == 0, + "Unexpected failure in key derivation", + raising=exc.RuntimeError, + ) + + return ffi.string(outbuf) + + +def crypto_pwhash_str_verify(passwd_hash: bytes, passwd: bytes) -> bool: + """ + Verifies the ``passwd`` against a given password hash. + + Returns True on success, raises InvalidkeyError on failure + :param passwd_hash: saved password hash + :type passwd_hash: bytes + :param passwd: password to be checked + :type passwd: bytes + :return: success + :rtype: boolean + """ + ensure(isinstance(passwd_hash, bytes), raising=TypeError) + ensure(isinstance(passwd, bytes), raising=TypeError) + ensure( + len(passwd_hash) <= 127, + "Hash must be at most 127 bytes long", + raising=exc.ValueError, + ) + + ret = lib.crypto_pwhash_str_verify(passwd_hash, passwd, len(passwd)) + + ensure(ret == 0, "Wrong password", raising=exc.InvalidkeyError) + # all went well, therefore: + return True + + +crypto_pwhash_argon2i_str_verify = crypto_pwhash_str_verify |