a fWcN @s dZddlmZmZddlmZddlmZddlZddl Z ddl Z e e Z ddlmZdadadadaddlmZddlmZmZmZdd lmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%dd l&m'Z'dd l(m)Z)dd l(m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/ddl0m1m2Z3d gZ4e*dZ5e*dZ6e*dZ7e*dZ8e*dZ9dZ:dZ;ddZe3j?e3j@e3jAe3jBZCGdddeCZDGdddeCZEGdddeCZFGdd d eCZGGd!d"d"eCZHGd#d$d$eCZIGd%d d eDeCZJe*d&ZKGd'd(d(eJZLGd)d*d*eLZMdS)+zpasslib.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 )with_statementabsolute_import) b64encode)sha256N)warn) compile_hmac)PasslibHashWarningPasslibSecurityWarningPasslibSecurityError) safe_crypt repeat_stringto_bytes parse_versionrng getrandstr test_crypt to_unicode utf8_truncateutf8_repeat_stringcrypt_accepts_bytes)bcrypt64)get_unbound_method_function)u uascii_to_strunicode str_to_uasciiPY3 error_frombcryptz$2$z$2a$z$2x$z$2y$z$2b$z<$2a$04$5BJqKfqMQvV7nS.yUguNcueVirQqDBGaLXSqj.rs.pZPlNR0UX/HKcCsLz ddl}Wnty YdS0zddlm}WntyFYdS0dS)a! internal helper which tries to distinguish pybcrypt vs bcrypt. :returns: True if cext-based py-bcrypt, False if ffi-based bcrypt, None if 'bcrypt' module not found. .. versionchanged:: 1.6.3 Now assuming bcrypt installed, unless py-bcrypt explicitly detected. Previous releases assumed py-bcrypt by default. Making this change since py-bcrypt is (apparently) unmaintained and static, whereas bcrypt is being actively maintained, and it's internal structure may shift. rN) __version__FT)r ImportErrorZbcrypt._bcryptr )rr r";/usr/lib/python3.9/site-packages/passlib/handlers/bcrypt.py_detect_pybcrypt7s   r$cs*eZdZdZdZdZdZejZ e Z e e eee fZede ede edeede iZd ZZejZd Zd Zd ZdZd ZdZdZdZdZdZdZ e Z!dZ"e#ddZ$ddZ%ddZ&e#fddZ'e#ddZ(e#fddZ)e#fddZ*d'fdd Z+d Z,e#d!d"Z-d#d$Z.e#d(d%d&Z/Z0S)) _BcryptCommona& Base class which implements brunt of BCrypt code. This is then subclassed by the various backends, to override w/ backend-specific methods. When a backend is loaded, the bases of the 'bcrypt' class proper are modified to prepend the correct backend-specific subclass. r)saltroundsidenttruncate_error22a2y2bz.Oeu Zlog2HFc Cs||\}}|tkrtd|td\}}t|}|td|fkrXtj|d|dd|dd}}||||p~d|dS)Nz>crypt_blowfish's buggy '2x' hashes are not currently supported$z%02dzmalformed cost fieldr/)r'r&checksumr() Z _parse_identIDENT_2X ValueErrorsplitrintuhexcMalformedHashError) clshashr(tailZ rounds_strdatar'r&chkr"r"r# from_stringsz_BcryptCommon.from_stringcCs$td|j|j|j|jf}t|S)Nz %s%02d$%s%s)rr(r'r&r4r)selfr=r"r"r# to_stringsz_BcryptCommon.to_stringcCstd||j|jf}t|S)z5internal helper to prepare config string for backendsz %s%02d$%s)rr'r&r)rBr(configr"r"r# _get_configsz_BcryptCommon._get_configc sHt|tr|d}|tr0|d|jvr0dStt|j|fi|S)NasciiT) isinstancebytesdecode startswithIDENT_2Afinal_salt_charssuperr% needs_update)r<r=kwds __class__r"r#rOs   z_BcryptCommon.needs_updatecCs ||r||S|SdS)z.safe_verifycsXd}|dd}|dd}||r:td|f||sTtd|fdS)a helper to check for cryptblowfish 8bit bug (fixed in 2y/2b); even though it's not known to be present in any of passlib's backends. this is treated as FATAL, because it can easily result in seriously malformed hashes, and we can't correct for it ourselves. test cases from reference hash is the incorrectly generated $2x$ hash taken from above url sёrFs805$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcASs805$6bNw2HLQYeqHYyBfLMsv/OUcZd0LKP39b87nBw3.S2tVZSqiQX6euzpasslib.hash.bcrypt: Your installation of the %r backend is vulnerable to the crypt_blowfish 8-bit bug (CVE-2011-2483) under %r hashes, and should be upgraded or replaced with another backendz(%s backend failed to verify %s 8bit hashN)encoder RuntimeErrorr(rcZbug_hashZ correct_hashrdrfr"r#assert_lacks_8bit_bugBs  zD_BcryptCommon._finalize_backend_mixin..assert_lacks_8bit_bugcsTddd}|dd}||r(dS|dd}||sPtd|fd S) a> check for bsd wraparound bug (fixed in 2b) this is treated as a warning, because it's rare in the field, and pybcrypt (as of 2015-7-21) is unpatched, but some people may be stuck with it. test cases from NOTE: reference hash is of password "0"*72 NOTE: if in future we need to deliberately create hashes which have this bug, can use something like 'hashpw(repeat_string(secret[:((1+secret) % 256) or 1]), 72)' s01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789NrFs804$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6Ts804$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oiz.%s backend failed to verify %s wraparound hashF)rhrirjrkr"r#detect_wrap_bugls   z>_BcryptCommon._finalize_backend_mixin..detect_wrap_bugcs |s dStd|fdS)Nz1%s backend unexpectedly has wraparound bug for %s)ri)r()rdrnr"r#assert_lacks_wrap_bugszD_BcryptCommon._finalize_backend_mixin..assert_lacks_wrap_bugs;$2$04$5BJqKfqMQvV7nS.yUguNcuRfMMOXK0xPWavM7pOzjEi5ze5T1k8/Stestz1%r backend lacks $2$ support, enabling workaroundz %s incorrectly rejected $2$ hashz %s lacks support for $2a$ hashesz!%s incorrectly rejected $2a$ hashos_cryptz;%r backend has $2a$ bsd wraparound bug, enabling workaroundzpasslib.hash.bcrypt: Your installation of the %r backend is vulnerable to the bsd wraparound bug, and should be upgraded or replaced with another backend (enabling workaround for now).r,r-z2%r backend lacks $2y$ support, enabling workaroundz!%s incorrectly rejected $2y$ hashr.z2%r backend lacks $2b$ support, enabling workaroundz!%s incorrectly rejected $2b$ hash)r_backend_mixin_map_workrounds_initializedrfr6r9r:ZMissingBackendError _bcryptorengineZ SaltErrorr__lacks_20_supportrarbri TEST_HASH_2ArLrr _has_2a_wraparound_bugreplace_lacks_2y_supportIDENT_2Y_lacks_2b_supportIDENT_2B_fallback_ident) mixin_clsrddryrunrgrlroZ test_hash_20resultZ test_hash_2yZ test_hash_2br")rdrnrerfr#_finalize_backend_mixinsp  *            z%_BcryptCommon._finalize_backend_mixincCs|j||j|jdS)z common helper for backends to implement _calc_checksum(). takes in secret, returns (secret, ident) pair, )new)_norm_digest_argsr(Z use_defaultsrBrcr"r"r#_prepare_digest_argssz"_BcryptCommon._prepare_digest_argscCs8|j}t|tr|d}n*|rFz|dWntyDd}Yn0t||r^||t |vrrtj ||j rt |dkr|rt|d}n |dd}|tkrn|tkr|jr|j}np|tkr|jr|j}nZ|tkr|jr0|r|rt|d}n t|d}|j}n |tkr$tdn td|||fS)Nutf-8Frmr2z.$2x$ hashes not currently supported by passlibzunexpected ident value: %r)_require_valid_utf8_bytesrHrrhrJUnicodeDecodeErrorr9Zvalidate_secret_check_truncate_policy_BNULLr:ZNullPasswordErrorrxlenrrLr}r|r~r{rzIDENT_2rvrr r5riAssertionError)r<rcr(rZrequire_valid_utf8_bytesr"r"r#rsH               z_BcryptCommon._norm_digest_args)F)F)1__name__ __module__ __qualname____doc__name setting_kwdsZ checksum_sizercharmapZchecksum_charsr} default_identrrLr5r{ ident_valuesr ident_aliasesZ min_salt_sizeZ max_salt_sizeZ salt_charsrMZdefault_roundsZ min_roundsZ max_roundsZ rounds_cost truncate_sizersrxrvrzr|r~r classmethodrArCrErOrUrWrXr\Z_no_backend_suggestionrrr __classcell__r"r"rQr#r%^sZ    Lr%cs eZdZdZfddZZS) _NoBackendz mixin used before any backend has been loaded. contains stubs that force loading of one of the available backends. cs|tt||SrV)Z_stub_requires_backendrNr_calc_checksumrrQr"r#rNsz_NoBackend._calc_checksum)rrrrrrr"r"rQr#rFsrc@s$eZdZdZeddZddZdS)_BcryptBackendz- backend which uses 'bcrypt' package cCsptr dSz ddlaWnty*YdS0z tjj}Wntjdddd}Yn0td|| ||S)NFrz&(trapped) error reading bcrypt versionTr] z%detected 'bcrypt' backend, version %r) r$r_bcryptr! __about__r rawarningrbr)rrrversionr"r"r#_load_backend_mixin`s     z"_BcryptBackend._load_backend_mixincCs||\}}||}t|tr,|d}t||}t|tsFJ||rdt |t |dkrxt j j |||dd|dd dS)NrFr*z`bcrypt` packagesource)rrErHrrhrhashpwrIrKrr9r:CryptBackendErrorrJrBrcr(rDr=r"r"r#rs    z_BcryptBackend._calc_checksumNrrrrrrrr"r"r"r#r[s %rc@s$eZdZdZeddZddZdS)_BcryptorBackendz/ backend which uses 'bcryptor' package cCs<z ddlaWnty YdS0|s0tdt|||S)NrFzqSupport for `bcryptor` is deprecated, and will be removed in Passlib 1.8; Please use `pip install bcrypt` instead)bcryptorrtr!rDeprecationWarningrrrrr"r"r#rs  z$_BcryptorBackend._load_backend_mixincCsn||\}}||}tjd||}||rJt|t|dkr^tj j |||ddt |ddS)NFr*zbcryptor libraryrr) rrErtruZEngineZhash_keyrKrr9r:rrrr"r"r#rs  z_BcryptorBackend._calc_checksumNrr"r"r"r#rs rc@s4eZdZdZdZeddZddZddZeZ dS) _PyBcryptBackendz/ backend which uses 'pybcrypt' package NcCsts dSz ddlaWnty*YdS0|s:tdtz tjj}Wntj dddd}Yn0t d|t |p|d }|d krtd |t j j|jdurddl}||_t|j|_|||S) NFrzrSupport for `py-bcrypt` is deprecated, and will be removed in Passlib 1.8; Please use `pip install bcrypt` insteadz((trapped) error reading pybcrypt versionTr]rz'detected 'pybcrypt' backend, version %r)rr)rzapy-bcrypt %s has a major security vulnerability, you should upgrade to py-bcrypt 0.3 immediately.)r$r _pybcryptr!rrrr rarrbrr9r:r _calc_lock threadingLockr_calc_checksum_threadsaferr)rrrrZvinforr"r"r#rs8         z$_PyBcryptBackend._load_backend_mixincCs4|j||WdS1s&0YdSrV)r_calc_checksum_rawrr"r"r#rsz*_PyBcryptBackend._calc_checksum_threadsafecCsf||\}}||}t||}||rBt|t|dkrVtjj|||ddt |ddS)Nr*zpybcrypt libraryrr) rrErrrKrr9r:rrrr"r"r#rs   z#_PyBcryptBackend._calc_checksum_raw) rrrrrrrrrrr"r"r"r#rs & rc@s*eZdZdZe ZeddZddZdS)_OsCryptBackendz0 backend which uses :func:`crypt.crypt` cCstdtsdS|||S)NrpF)rrwrrr"r"r#r s z#_OsCryptBackend._load_backend_mixincCs||\}}||}t||}|durd||rHt|t|dkrXtj||||ddStrt |t rz| dWn$t yt tjddYn0tjj}tjd||||fdS)Nr*rrzpython3 crypt.crypt() ony supports bytes passwords using UTF8; passlib recommends running `pip install bcrypt` for general bcrypt support.zcrypt.crypt() failed for unknown reason; passlib recommends running `pip install bcrypt` for general bcrypt support.(config=%s, secret=%s))rrEr rKrr9r:rrrHrIrJrrZPasswordValueErrordebug_only_reprr`)rBrcr(rDr=rr"r"r#rs,     z_OsCryptBackend._calc_checksumN) rrrrrrrrrr"r"r"r#rs  rc@s$eZdZdZeddZddZdS)_BuiltinBackendzA backend which uses passlib's pure-python implementation cCsBddlm}|tjds*tddSddlma | ||S)Nr)as_boolZPASSLIB_BUILTIN_BCRYPTz@bcrypt 'builtin' backend not enabled via $PASSLIB_BUILTIN_BCRYPTF) raw_bcrypt) passlib.utilsrosenvirongetrarbZpasslib.crypto._blowfishr_builtin_bcryptr)rrrrr"r"r#rSs    z#_BuiltinBackend._load_backend_mixincCs8||\}}t||dd|jd|j}|dS)NrF)rrr&rhr'rJ)rBrcr(r@r"r"r#r]s z_BuiltinBackend._calc_checksumNrr"r"r"r#rOs rc@s*eZdZdZdZdZeeee e e dZ dS)ra 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:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: :type salt: str :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]``. :type rounds: int :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}` -- increasing the rounds by +1 will double the amount of time taken. :type ident: str :param ident: Specifies which version of the BCrypt algorithm will be used when creating a new hash. Typically this option is not needed, as the default (``"2b"``) is usually the correct choice. If specified, it must be one of the following: * ``"2"`` - the first revision of BCrypt, which suffers from a minor security flaw and is generally not used anymore. * ``"2a"`` - some implementations suffered from rare security flaws, replaced by 2b. * ``"2y"`` - format specific to the *crypt_blowfish* BCrypt implementation, identical to ``"2b"`` in all but name. * ``"2b"`` - latest revision of the official BCrypt algorithm, current default. :param bool truncate_error: By default, BCrypt will silently truncate passwords larger than 72 bytes. Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. .. versionadded:: 1.7 :type relaxed: bool :param relaxed: By default, providing an invalid value for one of the other keywords will result in a :exc:`ValueError`. If ``relaxed=True``, and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` will be issued instead. Correctable errors include ``rounds`` that are too small or too large, and ``salt`` strings that are too long. .. versionadded:: 1.6 .. versionchanged:: 1.6 This class now supports ``"2y"`` hashes, and recognizes (but does not support) the broken ``"2x"`` hashes. (see the :ref:`crypt_blowfish bug ` for details). .. versionchanged:: 1.6 Added a pure-python backend. .. versionchanged:: 1.6.3 Added support for ``"2b"`` variant. .. versionchanged:: 1.7 Now defaults to ``"2b"`` variant. )rpybcryptrrqbuiltinT)NrrrrqrN) rrrrZbackendsZ_backend_mixin_targetrrrrrrrrr"r"r"r#rfsKr3c@s4eZdZdZeddejDZdZeddZ dS)_wrapped_bcryptz abstracts out some bits bcrypt_sha256 & django_bcrypt_sha256 share. - bypass backend-loading wrappers for hash() etc - disable truncation support, sha256 wrappers don't need it. ccs|]}|dvr|VqdS))r)Nr").0elemr"r"r# z_wrapped_bcrypt.NcCsdSrVr")r<rcr"r"r#rsz&_wrapped_bcrypt._check_truncate_policy) rrrrtuplerrrrrr"r"r"r#rs rcseZdZdZdZeefZddeZeZ e ddgZ dZ e dfdd Zed Zed Zed Ze d d Ze ddZedZedZddZdfdd Ze ddZfddZfddZZS) bcrypt_sha256a This class implements a composition of BCrypt + HMAC_SHA256, and follows the :ref:`password-hash-api`. It supports a fixed-length salt, and a variable number of rounds. The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept all the same optional keywords as the base :class:`bcrypt` hash. .. versionadded:: 1.6.2 .. versionchanged:: 1.7 Now defaults to ``"2b"`` bcrypt variant; though supports older hashes generated using the ``"2a"`` bcrypt variant. .. versionchanged:: 1.7.3 For increased security, updated to use HMAC-SHA256 instead of plain SHA256. Now only supports the ``"2b"`` bcrypt variant. Hash format updated to "v=2". cstfddtjDS)Nc3s|]}|dvr|VqdS)rNr")ritemrr"r#rs z)bcrypt_sha256...)dictrritemsrr"rr#rzbcrypt_sha256.rNc sXtt|jfi|}|dur*|||_|j}|jdkrT|tkrTtd||jf|S)Nrz+bcrypt %r hashes not allowed for version %r)rNrusing _norm_versionrrr}r6)r<rrPsubclsr(rQr"r#r,s zbcrypt_sha256.usingz$bcrypt-sha256$z(?x) ^ [$]bcrypt-sha256[$] v=(?P\d+), t=(?P2b), r=(?P\d{1,2}) [$](?P[^$]{22}) (?:[$](?P[^$]{31}))? $ z(?x) ^ [$]bcrypt-sha256[$] (?P2[ab]), (?P\d{1,2}) [$](?P[^$]{22}) (?:[$](?P[^$]{31}))? $ cCst|}|sdS||jS)NF)r9Zto_unicode_for_identifyrKprefixrTr"r"r#rSas zbcrypt_sha256.identifycCst|dd}||js$tj||j|}|rXt| d}|dkrztj |n"|j |}|rnd}n tj || d}|tj r|tj krtj |||| dt|| d| d d S) NrFr=rrrr'typer&digest)rr(r'r&r4)rrKrr9r:ZInvalidHashError _v2_hash_rematchr8groupr; _v1_hash_reZ_UZEROZZeroPaddedRoundsError)r<r=mrr'r"r"r#rAhs,        zbcrypt_sha256.from_stringz"$bcrypt-sha256$v=2,t=%s,r=%d$%s$%sz$bcrypt-sha256$%s,%d$%s$%scCs>|jdkr|j}n|j}||jt|j|j|jf}t |S)Nr) r _v1_template _v2_templater(strip_UDOLLARr'r&r4r)rBtemplater=r"r"r#rCs  zbcrypt_sha256.to_stringc s.|dur|||_tt|jfi|dSrV)rrrNr__init__)rBrrPrQr"r#rs zbcrypt_sha256.__init__cCs ||jvrtd|j|f|S)Nz&%s: unknown or unsupported version: %r)_supported_versionsr6r)r<rr"r"r#rs zbcrypt_sha256._norm_versioncstt|tr|d}|jdkr,t|}n0|j}|d|jvrHtdt d|d|}t |}t t | |S)Nrrrzinvalid salt stringrrF)rHrrhrrrr&rMr6rrrNrr)rBrcrr&keyrQr"r#rs    zbcrypt_sha256._calc_checksumc s*|jt|jkrdStt|jfi|S)NT)rrrNr_calc_needs_update)rBrPrQr"r#rsz bcrypt_sha256._calc_needs_update)N)N)rrrrrrLr}rrrsetrrrrrrrecompilerrrSrArrrCrrrrrr"r"rQr#rs4       +r)NrZ __future__rrbase64rZhashlibrrrZloggingZ getLoggerrrawarningsrrrrtrZpasslib.crypto.digestrZ passlib.excrr r rr r r rrrrrrrrZpasslib.utils.binaryrZpasslib.utils.compatrrrrrrrZpasslib.utils.handlersZutilshandlersr9__all__rrLr5r{r}rrwr$ZSubclassBackendMixinZ TruncateMixinZ HasManyIdentsZ HasRoundsZHasSaltZGenericHandlerr%rrrrrrrrrrr"r"r"r#sX     4   ' k=$HKb#