about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/nacl/bindings/crypto_pwhash.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
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.py600
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