"""passlib.bcrypt -- implementation of OpenBSD's BCrypt algorithm.
TODO:
* support 2x and altered-2a hashes?
http://www.openwall.com/lists/oss-security/2011/06/27/9
* deal with lack of PY3-compatibile c-ext implementation
"""
#=========================================================
#imports
#=========================================================
from __future__ import with_statement, absolute_import
#core
import os
import re
import logging; log = logging.getLogger(__name__)
from warnings import warn
#site
try:
from bcrypt import hashpw as pybcrypt_hashpw
except ImportError: #pragma: no cover
pybcrypt_hashpw = None
try:
from bcryptor.engine import Engine as bcryptor_engine
except ImportError: #pragma: no cover
bcryptor_engine = None
#libs
from passlib.exc import PasslibHashWarning, PasslibSecurityWarning
from passlib.utils import bcrypt64, safe_crypt, repeat_string, \
classproperty, rng, getrandstr, test_crypt
from passlib.utils.compat import bytes, u, uascii_to_str, unicode, str_to_uascii
import passlib.utils.handlers as uh
#pkg
#local
__all__ = [
"bcrypt",
]
#=========================================================
# support funcs & constants
#=========================================================
_builtin_bcrypt = None
def _load_builtin():
global _builtin_bcrypt
if _builtin_bcrypt is None:
from passlib.utils._blowfish import raw_bcrypt as _builtin_bcrypt
IDENT_2 = u("$2$")
IDENT_2A = u("$2a$")
IDENT_2X = u("$2x$")
IDENT_2Y = u("$2y$")
#=========================================================
# handler
#=========================================================
class bcrypt(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.HasManyBackends, uh.GenericHandler):
"""This class implements the BCrypt password hash, and follows the :ref:`password-hash-api`.
It supports a fixed-length salt, and a variable number of rounds.
The :meth:`encrypt()` and :meth:`genconfig` methods accept the following optional keywords:
:param salt:
Optional salt string.
If not specified, one will be autogenerated (this is recommended).
If specified, it must be 22 characters, drawn from the regexp range ``[./0-9A-Za-z]``.
:param rounds:
Optional number of rounds to use.
Defaults to 12, must be between 4 and 31, inclusive.
This value is logarithmic, the actual number of iterations used will be :samp:`2**{rounds}`.
:param ident:
selects specific version of BCrypt hash that will be used.
Typically you want to leave this alone, and let it default to ``2a``,
but it can be set to ``2`` to use the older version of BCrypt.
It will use the first available of three possible backends:
1. `py-bcrypt `_, if installed.
2. `bcryptor `_, if installed.
3. stdlib's :func:`crypt.crypt()`, if the host OS supports BCrypt (eg: BSD).
If no backends are available at runtime,
:exc:`~passlib.exc.MissingBackendError` will be raised
whenever :meth:`encrypt` or :meth:`verify` are called.
You can see which backend is in use by calling the
:meth:`~passlib.utils.handlers.HasManyBackends.get_backend()` method.
"""
#=========================================================
#class attrs
#=========================================================
#--GenericHandler--
name = "bcrypt"
setting_kwds = ("salt", "rounds", "ident")
checksum_size = 31
checksum_chars = bcrypt64.charmap
#--HasManyIdents--
default_ident = u("$2a$")
ident_values = (u("$2$"), IDENT_2A, IDENT_2X, IDENT_2Y)
ident_aliases = {u("2"): u("$2$"), u("2a"): IDENT_2A, u("2y"): IDENT_2Y}
#--HasSalt--
min_salt_size = max_salt_size = 22
salt_chars = bcrypt64.charmap
#NOTE: 22nd salt char must be in bcrypt64._padinfo2[1], not full charmap
#--HasRounds--
default_rounds = 12 # current passlib default
min_rounds = 4 # bcrypt spec specified minimum
max_rounds = 31 # 32-bit integer limit (since real_rounds=1<