123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- # 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
|